leaflet-rotate 0.4.0 → 0.5.0

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,17 +1,64 @@
1
1
  /**
2
+ * Extends Leaflet maps with bearing rotation and 3D pitch.
3
+ *
2
4
  * @external L.Map
3
5
  *
6
+ * @since leaflet-rotate (v0.1.0)
7
+ *
4
8
  * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/Map.js
5
9
  */
6
10
 
11
+ /**
12
+ * @event L.Map#rotate Fired after the map bearing changes.
13
+ *
14
+ * @since leaflet-rotate (v0.1.0)
15
+ */
16
+
17
+ /**
18
+ * @event L.Map#pitch Fired after the map pitch changes.
19
+ *
20
+ * @since leaflet-rotate (0.5.0)
21
+ */
22
+
7
23
  import { toPoint } from '../geometry/Point';
8
24
 
9
25
  const mapProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Map.prototype));
10
26
 
11
- L.Map.mergeOptions({ rotate: false, bearing: 0, });
27
+ L.Map.mergeOptions({
28
+
29
+ /**
30
+ * @type {boolean} Enable bearing and pitch transformations.
31
+ *
32
+ * @since leaflet-rotate (v0.1.0)
33
+ */
34
+ rotate: false,
35
+
36
+ /**
37
+ * @type {number} Initial map bearing in degrees.
38
+ *
39
+ * @since leaflet-rotate (v0.1.0)
40
+ */
41
+ bearing: 0,
42
+
43
+ /**
44
+ * @type {number} Initial map pitch in degrees, clamped to 0–85.
45
+ *
46
+ * @since leaflet-rotate (0.5.0)
47
+ */
48
+ pitch: 0,
49
+
50
+ });
12
51
 
13
52
  L.Map.include({
14
53
 
54
+ /**
55
+ * Apply an immediate animated-zoom frame without waiting for a transition delay.
56
+ *
57
+ * @param {L.LatLng} center Target center.
58
+ * @param {number} zoom Target zoom level.
59
+ *
60
+ * @since leaflet-rotate (v0.1.0)
61
+ */
15
62
  _animateZoomNoDelay(center, zoom) {
16
63
  if (!this._mapPane) { return; }
17
64
 
@@ -23,17 +70,24 @@ L.Map.include({
23
70
  },
24
71
 
25
72
  /**
26
- * @param {(HTMLElement|String)} id html selector
27
- * @param {Object} [options={}] leaflet map options
73
+ * Initialize the Leaflet map and apply its initial bearing and pitch.
74
+ *
75
+ * @param {HTMLElement|string} id Map container element or its ID.
76
+ * @param {Object} [options={}] Leaflet map options, including `rotate`, `bearing`, and `pitch`.
77
+ *
78
+ * @since leaflet-rotate (v0.1.0)
28
79
  */
29
80
  initialize(id, options) {
30
81
  if (options.rotate) {
31
82
  this._rotate = true;
32
83
  this._bearing = 0;
84
+ this._pitch = 0;
33
85
  }
34
86
  mapProto.initialize.apply(this, arguments);
35
- if(this.options.rotate){
36
- this.setBearing(this.options.bearing);
87
+ if (this.options.rotate) {
88
+ this.on('resize', this._updatePitchTransform, this);
89
+ this.setBearing(this.options.bearing);
90
+ this.setPitch(this.options.pitch);
37
91
  }
38
92
  },
39
93
 
@@ -42,13 +96,22 @@ L.Map.include({
42
96
  * returns the corresponding pixel coordinate relative to
43
97
  * the [origin pixel](#map-getpixelorigin).
44
98
  *
45
- * @param {L.Point} point pixel screen coordinates
46
- * @returns {L.Point} transformed pixel point
99
+ * @param {L.Point|number[]} point Pixel coordinate in the map container.
100
+ *
101
+ * @returns {L.Point} Corresponding layer-point coordinate.
102
+ *
103
+ * @since leaflet-rotate (v0.1.0)
47
104
  */
48
105
  containerPointToLayerPoint(point) {
49
106
  if (!this._rotate) {
50
107
  return mapProto.containerPointToLayerPoint.apply(this, arguments);
51
108
  }
109
+ if (this._pitch) {
110
+ return this._transformPitchPoint(
111
+ toPoint(point).subtract(this._getMapPanePos()),
112
+ this._pitchInverseTransform
113
+ );
114
+ }
52
115
  return toPoint(point)
53
116
  .subtract(this._getMapPanePos())
54
117
  .rotateFrom(-this._bearing, this._getRotatePanePos())
@@ -59,13 +122,19 @@ L.Map.include({
59
122
  * Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
60
123
  * returns the corresponding pixel coordinate relative to the map container.
61
124
  *
62
- * @param {L.Point} point pixel screen coordinates
63
- * @returns {L.Point} transformed pixel point
125
+ * @param {L.Point|number[]} point Pixel coordinate in layer-point space.
126
+ *
127
+ * @returns {L.Point} Corresponding pixel coordinate in the map container.
128
+ *
129
+ * @since leaflet-rotate (v0.1.0)
64
130
  */
65
131
  layerPointToContainerPoint(point) {
66
132
  if (!this._rotate) {
67
133
  return mapProto.layerPointToContainerPoint.apply(this, arguments);
68
134
  }
135
+ if (this._pitch) {
136
+ return this._transformPitchPoint(point, this._pitchTransform).add(this._getMapPanePos());
137
+ }
69
138
  return toPoint(point)
70
139
  .add(this._getRotatePanePos())
71
140
  .rotateFrom(this._bearing, this._getRotatePanePos())
@@ -79,12 +148,16 @@ L.Map.include({
79
148
  * (rotatePane) --> (mapPane)
80
149
  * (rotatePane) --> (norotatePane)
81
150
  *
82
- * @param {L.Point} point pixel screen coordinates
83
- * @returns {L.Point}
151
+ * @param {L.Point|number[]} point Point in the rotated pane.
152
+ *
153
+ * @returns {L.Point} Corresponding point in map-pane coordinates.
84
154
  *
85
- * @since leaflet-rotate (v0.1)
155
+ * @since leaflet-rotate (v0.1.0)
86
156
  */
87
157
  rotatedPointToMapPanePoint(point) {
158
+ if (this._pitch) {
159
+ return this._transformPitchPoint(point, this._pitchTransform);
160
+ }
88
161
  return toPoint(point)
89
162
  .rotate(this._bearing)
90
163
  ._add(this._getRotatePanePos());
@@ -97,11 +170,16 @@ L.Map.include({
97
170
  * (mapPane) --> (rotatePane)
98
171
  * (norotatePane) --> (rotatePane)
99
172
  *
100
- * @param {L.Point} point pixel screen coordinates
173
+ * @param {L.Point|number[]} point Point in map-pane coordinates.
101
174
  *
102
- * @since leaflet-rotate (v0.1)
175
+ * @returns {L.Point} Corresponding point in the rotated pane.
176
+ *
177
+ * @since leaflet-rotate (v0.1.0)
103
178
  */
104
179
  mapPanePointToRotatedPoint(point) {
180
+ if (this._pitch) {
181
+ return this._transformPitchPoint(point, this._pitchInverseTransform);
182
+ }
105
183
  return toPoint(point)
106
184
  ._subtract(this._getRotatePanePos())
107
185
  .rotate(-this._bearing);
@@ -122,10 +200,11 @@ L.Map.include({
122
200
  *
123
201
  * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/map/Map.js#L1114-L1135
124
202
  *
125
- * @param {L.LatLngBounds} bounds
126
- * @returns {L.Bounds}
203
+ * @param {L.LatLngBounds} bounds Geographic bounds to project.
204
+ *
205
+ * @returns {L.Bounds} Axis-aligned bounds in container-pixel coordinates.
127
206
  *
128
- * @since leaflet-rotate (v0.2)
207
+ * @since leaflet-rotate (v0.2.0)
129
208
  */
130
209
  mapBoundsToContainerBounds(bounds) {
131
210
  if (!this._rotate && mapProto.mapBoundsToContainerBounds) {
@@ -151,14 +230,13 @@ L.Map.include({
151
230
  },
152
231
 
153
232
  /**
154
- * Returns geographical bounds visible in the current map view
233
+ * Return the geographic bounds of the current viewport, accounting for rotation and pitch.
155
234
  *
156
- * @TODO find out if map bounds calculated by `L.Map::getBounds()`
157
- * function should match the `rotatePane` or `norotatePane` bounds
235
+ * The result is the smallest geographic bounds containing all four viewport corners.
158
236
  *
159
- * @see https://github.com/fnicollet/Leaflet/issues/7
237
+ * @returns {L.LatLngBounds} Geographic bounds visible in the map viewport.
160
238
  *
161
- * @returns {L.LatLngBounds}
239
+ * @since leaflet-rotate (v0.1.0)
162
240
  */
163
241
  getBounds() {
164
242
  if (!this._rotate) {
@@ -202,15 +280,19 @@ L.Map.include({
202
280
  // },
203
281
 
204
282
  /**
205
- * Change map rotation
283
+ * Set the map bearing, optionally animating to the new value.
206
284
  *
207
- * @param {number} theta map degrees
208
- * @param {Object} [options] animation options
209
- * @param {boolean} [options.animate=false]
210
- * @param {number} [options.duration=0.25] animation duration in seconds
211
- * @param {number} [options.easeLinearity=0.5]
285
+ * @param {number} theta Target bearing in degrees; normalized to [0, 360).
286
+ * @param {Object} [options] Animation options.
287
+ * @param {boolean} [options.animate=false] Animate the change.
288
+ * @param {number} [options.duration=0.25] Animation duration in seconds.
289
+ * @param {number} [options.easeLinearity=0.5] Easing curve linearity.
212
290
  *
213
- * @since leaflet-rotate (v0.1)
291
+ * @returns {void}
292
+ *
293
+ * @fires L.Map#rotate
294
+ *
295
+ * @since leaflet-rotate (v0.1.0)
214
296
  */
215
297
  setBearing(theta, options) {
216
298
  if (!this._rotate) { return; }
@@ -260,28 +342,169 @@ L.Map.include({
260
342
  this._setBearing(theta);
261
343
  },
262
344
 
263
- _setBearing(theta) {
345
+ /**
346
+ * Apply a bearing immediately and update the rotated pane and map coordinates.
347
+ *
348
+ * @param {number} theta Bearing in degrees.
349
+ * @param {L.LatLng} [mapCenter] Center to preserve while applying the change.
350
+ *
351
+ * @fires L.Map#rotate
352
+ *
353
+ * @since leaflet-rotate (v0.1.0)
354
+ */
355
+ _setBearing(theta, mapCenter) {
356
+ if (!mapCenter && this._loaded) {
357
+ mapCenter = this.getCenter();
358
+ }
359
+
264
360
  const bearing = L.Util.wrapNum(theta, [0, 360]) * (Math.PI / 180),
265
361
  center = this._getPixelCenter(),
266
362
  oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center),
267
363
  newPos = oldPos.rotateFrom(bearing, center);
268
364
 
269
- // CSS transform
270
- L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center);
271
-
272
365
  this._pivot = center;
273
366
  this._bearing = bearing;
274
367
  this._rotatePanePos = newPos;
275
368
 
369
+ if (this._pitch) {
370
+ this._rotatePane._leaflet_pos = oldPos;
371
+ this._updatePitchTransform();
372
+ } else {
373
+ L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center);
374
+ }
375
+
376
+ if (mapCenter) {
377
+ this._pixelOrigin = this.project(mapCenter, this._zoom)
378
+ .subtract(this.containerPointToLayerPoint(this.getSize().divideBy(2)));
379
+ delete this._lastCenter;
380
+ this.fire('move');
381
+ }
382
+
276
383
  this.fire('rotate');
277
384
  },
278
385
 
279
386
  /**
280
- * Get current map rotation
387
+ * Transform a point with a projective matrix, accepting DOMMatrix or its 2D inverse.
388
+ *
389
+ * @param {L.Point|number[]} point Input pixel coordinate.
390
+ * @param {DOMMatrix|Object} matrix Forward or inverse transformation matrix.
391
+ *
392
+ * @returns {L.Point} Transformed pixel coordinate.
393
+ *
394
+ * @since leaflet-rotate (0.5.0)
395
+ */
396
+ _transformPitchPoint(point, matrix) {
397
+ point = toPoint(point);
398
+ if (!(matrix instanceof DOMMatrix)) {
399
+ const denominator = matrix.g * point.x + matrix.h * point.y + matrix.i;
400
+ return new L.Point(
401
+ (matrix.a * point.x + matrix.b * point.y + matrix.c) / denominator,
402
+ (matrix.d * point.x + matrix.e * point.y + matrix.f) / denominator
403
+ );
404
+ }
405
+ const transformed = new DOMPoint(point.x, point.y).matrixTransform(matrix);
406
+ return new L.Point(transformed.x / transformed.w, transformed.y / transformed.w);
407
+ },
408
+
409
+ /**
410
+ * Apply the current pitch/bearing CSS transform and cache its forward and inverse matrices.
411
+ *
412
+ * @since leaflet-rotate (0.5.0)
413
+ */
414
+ _updatePitchTransform() {
415
+ if (!this._pitch || !this._rotatePane) { return; }
416
+
417
+ const pivot = this._getPixelCenter(),
418
+ size = this.getSize(),
419
+ perspective = Math.max(1, size.y * 1.5),
420
+ position = this._rotatePane._leaflet_pos || new L.Point(0, 0),
421
+ transform = `translate3d(${pivot.x}px,${pivot.y}px,0) perspective(${perspective}px) rotateX(${this.getPitch()}deg) rotateZ(${this.getBearing()}deg) translate3d(${position.x - pivot.x}px,${position.y - pivot.y}px,0)`;
422
+
423
+ this._rotatePane.style.transformOrigin = '0 0';
424
+ this._rotatePane.style.transform = transform;
425
+ const matrix = this._pitchTransform = new DOMMatrix(getComputedStyle(this._rotatePane).transform),
426
+ a = matrix.m11, b = matrix.m21, c = matrix.m41,
427
+ d = matrix.m12, e = matrix.m22, f = matrix.m42,
428
+ g = matrix.m14, h = matrix.m24, i = matrix.m44,
429
+ determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
430
+
431
+ this._pitchInverseTransform = {
432
+ a: (e * i - f * h) / determinant,
433
+ b: (c * h - b * i) / determinant,
434
+ c: (b * f - c * e) / determinant,
435
+ d: (f * g - d * i) / determinant,
436
+ e: (a * i - c * g) / determinant,
437
+ f: (c * d - a * f) / determinant,
438
+ g: (d * h - e * g) / determinant,
439
+ h: (b * g - a * h) / determinant,
440
+ i: (a * e - b * d) / determinant,
441
+ };
442
+ },
443
+
444
+ /**
445
+ * Set the map pitch in degrees. Values are clamped to the range 0–85.
446
+ *
447
+ * @param {number} pitch Pitch angle in degrees; clamped to [0, 85].
448
+ *
449
+ * @returns {L.Map} This map instance.
450
+ *
451
+ * @fires L.Map#pitch
452
+ *
453
+ * @since leaflet-rotate (0.5.0)
454
+ */
455
+ setPitch(pitch) {
456
+ this._setPitchAndBearing(pitch, this.getBearing());
457
+ return this;
458
+ },
459
+
460
+ /**
461
+ * Update pitch and bearing together, preserving the current map center.
462
+ * Used by the Ctrl+drag handler to avoid applying two separate transforms.
463
+ *
464
+ * @param {number} pitch Pitch angle in degrees.
465
+ * @param {number} bearing Bearing angle in degrees.
466
+ *
467
+ * @fires L.Map#pitch
468
+ * @fires L.Map#rotate
469
+ *
470
+ * @since leaflet-rotate (0.5.0)
471
+ */
472
+ _setPitchAndBearing(pitch, bearing) {
473
+ if (!this._rotate) { return; }
474
+
475
+ const clampedPitch = Math.max(0, Math.min(pitch, 85)) * (Math.PI / 180),
476
+ clampedBearing = L.Util.wrapNum(bearing, [0, 360]) * (Math.PI / 180),
477
+ pitchChanged = clampedPitch !== this._pitch;
478
+ if (!pitchChanged && clampedBearing === this._bearing) { return; }
479
+
480
+ const mapCenter = this._loaded ? this.getCenter() : undefined;
481
+ if (pitchChanged) {
482
+ this._pitch = clampedPitch;
483
+ this._rotatePane.style.transformOrigin = L.version.startsWith('2.') ? '0 0' : '';
484
+ }
485
+ this._setBearing(bearing, mapCenter);
486
+ if (pitchChanged) {
487
+ this.fire('pitch');
488
+ }
489
+ },
490
+
491
+ /**
492
+ * Get the current map pitch in degrees.
493
+ *
494
+ * @returns {number} Pitch angle in degrees.
495
+ *
496
+ * @since leaflet-rotate (0.5.0)
497
+ */
498
+ getPitch() {
499
+ return (this._pitch || 0) * (180 / Math.PI);
500
+ },
501
+
502
+ /**
503
+ * Get the current map bearing. Enable the `rotate` map option before calling.
281
504
  *
282
- * @returns {number} theta map degrees
505
+ * @returns {number} Bearing in degrees, normalized to [0, 360).
283
506
  *
284
- * @since leaflet-rotate (v0.1)
507
+ * @since leaflet-rotate (v0.1.0)
285
508
  */
286
509
  getBearing() {
287
510
  return this._bearing * (180 / Math.PI);
@@ -294,7 +517,10 @@ L.Map.include({
294
517
  *
295
518
  * @param {String} name leaflet pane
296
519
  * @param {HTMLElement} [container] parent element
520
+ *
297
521
  * @returns {HTMLElement} pane container
522
+ *
523
+ * @since leaflet-rotate (v0.1.0)
298
524
  */
299
525
  // createPane(name, container) {
300
526
  // if (!this._rotate || name == 'mapPane') {
@@ -325,6 +551,12 @@ L.Map.include({
325
551
  * - tooltipPane [HTMLElement = 650] - Pane for tooltips.
326
552
  * - popupPane [HTMLElement = 700] - Pane for popups.
327
553
  */
554
+ /**
555
+ * Create the map's panes, placing tile/vector layers in the transformed pane
556
+ * and markers/overlays in the pane that remains upright.
557
+ *
558
+ * @since leaflet-rotate (v0.1.0)
559
+ */
328
560
  _initPanes() {
329
561
  const panes = this._panes = {};
330
562
  this._paneRenderers = {};
@@ -369,13 +601,15 @@ L.Map.include({
369
601
  *
370
602
  * @see https://github.com/Raruto/leaflet-rotate/issues/18
371
603
  *
372
- * @param {L.LatLng} latlng coordinates
373
- * @param {Object} [options={}] padding options
604
+ * @param {L.LatLng} latlng Geographic point to keep visible.
605
+ * @param {Object} [options={}] Padding options (`padding`, `paddingTopLeft`, `paddingBottomRight`).
374
606
  *
375
607
  * @returns {L.Map} current map instance
608
+ *
609
+ * @since leaflet-rotate (v0.1.4)
376
610
  */
377
611
  panInside(latlng, options) {
378
- if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
612
+ if (!this._rotate || (!this._pitch && Math.abs(this._bearing).toFixed(1) < 0.1)) {
379
613
  return mapProto.panInside.apply(this, arguments);
380
614
  }
381
615
 
@@ -419,9 +653,12 @@ L.Map.include({
419
653
  *
420
654
  * @see https://github.com/fnicollet/Leaflet/issues/7
421
655
  *
422
- * @param {L.LatLngBounds} bounds coordinates
423
- * @param {Object} [options] pan options
424
- * @returns {L.Map} current map instance
656
+ * @param {L.LatLngBounds} bounds Geographic bounds to fit inside the viewport.
657
+ * @param {Object} [options] Pan animation options.
658
+ *
659
+ * @returns {L.Map} This map instance.
660
+ *
661
+ * @since leaflet-rotate (v0.1.0)
425
662
  */
426
663
  // panInsideBounds(bounds, options) {
427
664
  // this._enforcingBounds = true;
@@ -490,14 +727,16 @@ L.Map.include({
490
727
  * instead returns the minimum zoom level on which the map view fits into
491
728
  * the given bounds in its entirety.
492
729
  *
493
- * @param {L.LatLngBounds} bounds
494
- * @param {Boolean} [inside=false]
495
- * @param {L.Point} [padding=[0,0]]
730
+ * @param {L.LatLngBounds} bounds Geographic bounds to fit.
731
+ * @param {boolean} [inside=false] Fit the viewport inside bounds instead of bounds inside the viewport.
732
+ * @param {L.Point|number[]} [padding=[0,0]] Pixel padding around the viewport.
496
733
  *
497
- * @returns {Number} zoom level
734
+ * @returns {number} Zoom level satisfying the fit.
735
+ *
736
+ * @since leaflet-rotate (v0.1.0)
498
737
  */
499
738
  getBoundsZoom(bounds, inside, padding) {
500
- if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
739
+ if (!this._rotate || (!this._pitch && Math.abs(this._bearing).toFixed(1) < 0.1)) {
501
740
  return mapProto.getBoundsZoom.apply(this, arguments);
502
741
  }
503
742
 
@@ -539,20 +778,32 @@ L.Map.include({
539
778
  // },
540
779
 
541
780
  /**
542
- * Offset of the specified place to the current center in pixels
781
+ * Return the pixel offset from the current center to the given geographic point.
782
+ *
783
+ * @param {L.LatLng} latlng Geographic point.
543
784
  *
544
- * @param {L.LatLng} latlng map coordinates
785
+ * @returns {L.Point} Offset in layer pixels, adjusted for bearing and pitch.
786
+ *
787
+ * @since leaflet-rotate (v0.1.0)
545
788
  */
546
789
  _getCenterOffset(latlng) {
547
790
  let centerOffset = mapProto._getCenterOffset.apply(this, arguments);
548
- if (this._rotate) {
791
+ if (this._pitch) {
792
+ const center = this.getSize()._divideBy(2);
793
+ centerOffset = this.containerPointToLayerPoint(center.add(centerOffset))
794
+ .subtract(this.containerPointToLayerPoint(center));
795
+ } else if (this._rotate) {
549
796
  centerOffset = centerOffset.rotate(this._bearing);
550
797
  }
551
798
  return centerOffset;
552
799
  },
553
800
 
554
801
  /**
555
- * @since leaflet-rotate (v0.1)
802
+ * Return the current position of the rotated pane.
803
+ *
804
+ * @returns {L.Point} Rotated-pane position in map-pane coordinates.
805
+ *
806
+ * @since leaflet-rotate (v0.1.0)
556
807
  */
557
808
  _getRotatePanePos() {
558
809
  return this._rotatePanePos || new L.Point(0, 0);
@@ -564,6 +815,16 @@ L.Map.include({
564
815
  // return this.project(latlng, zoom)._subtract(topLeft);
565
816
  //},
566
817
 
818
+ /**
819
+ * Calculate the pixel origin for a target view while preserving bearing-aware alignment.
820
+ *
821
+ * @param {L.LatLng} center Target center.
822
+ * @param {number} zoom Target zoom level.
823
+ *
824
+ * @returns {L.Point} New pixel origin.
825
+ *
826
+ * @since leaflet-rotate (v0.1.0)
827
+ */
567
828
  _getNewPixelOrigin(center, zoom) {
568
829
  if (!this._rotate || L.version.startsWith('2.')) {
569
830
  return mapProto._getNewPixelOrigin.apply(this, arguments);
@@ -579,7 +840,14 @@ L.Map.include({
579
840
  },
580
841
 
581
842
  /**
582
- * @since leaflet-rotate (v0.2)
843
+ * Calculate the pixel bounds needed to cover the transformed viewport.
844
+ *
845
+ * @param {L.LatLng} [center=this.getCenter()] Target center.
846
+ * @param {number} [zoom=this.getZoom()] Target zoom level.
847
+ *
848
+ * @returns {L.Bounds} Pixel bounds in projected map coordinates.
849
+ *
850
+ * @since leaflet-rotate (v0.2.0)
583
851
  *
584
852
  * @see src\layer\tile\GridLayer::_getTiledPixelBounds()
585
853
  */
@@ -604,9 +872,9 @@ L.Map.include({
604
872
  },
605
873
 
606
874
  /**
607
- * @since leaflet-rotate (v0.2)
875
+ * @since leaflet-rotate (v0.2.0)
608
876
  *
609
- * @return {L.Point} map pivot point (center)
877
+ * @returns {L.Point} Map pivot point in pixel coordinates.
610
878
  */
611
879
  _getPixelCenter() {
612
880
  if (!this._rotate && mapProto._getPixelCenter) {
@@ -616,9 +884,13 @@ L.Map.include({
616
884
  },
617
885
 
618
886
  /**
619
- * @since leaflet-rotate (v0.2)
620
- *
621
887
  * @see src\layer\vector\Renderer::_update()
888
+ *
889
+ * @param {number} padding Bounds padding as a fraction of map size.
890
+ *
891
+ * @returns {L.Bounds} Padded bounds in layer-point coordinates.
892
+ *
893
+ * @since leaflet-rotate (v0.2.0)
622
894
  */
623
895
  _getPaddedPixelBounds(padding) {
624
896
  if (!this._rotate && mapProto._getPaddedPixelBounds) {
@@ -638,6 +910,15 @@ L.Map.include({
638
910
  ]);
639
911
  },
640
912
 
913
+ /**
914
+ * Build and fire Leaflet's location-found event, including the device heading.
915
+ *
916
+ * @param {GeolocationPosition} pos Browser geolocation result.
917
+ *
918
+ * @fires L.Map#locationfound
919
+ *
920
+ * @since leaflet-rotate (v0.1.0)
921
+ */
641
922
  _handleGeolocationResponse(pos) {
642
923
  if (!this._container._leaflet_id) { return; }
643
924
 
@@ -678,6 +959,7 @@ L.Map.include({
678
959
  * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/geo/LatLngBounds.js#L253-L264
679
960
  *
680
961
  * @param {L.Bounds} points
962
+ *
681
963
  * @returns {L.Bounds}
682
964
  */
683
965
  // toCircumscribedBounds(points) {
@@ -1,11 +1,19 @@
1
1
  /**
2
- * Rotates the map according to a smartphone's compass.
2
+ * Sets map bearing from device orientation when the browser exposes compass data.
3
3
  *
4
4
  * @typedef L.Map.CompassBearing
5
+ * @property {L.Map.CompassBearing} compassBearing Map handler, enabled to follow device heading.
6
+ *
7
+ * @since leaflet-rotate (v0.1.0)
5
8
  */
6
9
 
7
10
  L.Map.CompassBearing = L.Handler.extend({
8
11
 
12
+ /**
13
+ * @param {L.Map} map Map whose bearing follows device orientation.
14
+ *
15
+ * @since leaflet-rotate (v0.1.0)
16
+ */
9
17
  initialize(map) {
10
18
  this._map = map;
11
19
  /** @see https://caniuse.com/?search=DeviceOrientation */
@@ -17,6 +25,11 @@ L.Map.CompassBearing = L.Handler.extend({
17
25
  this._throttled = L.Util.throttle(this._onDeviceOrientation, 100, this);
18
26
  },
19
27
 
28
+ /**
29
+ * Subscribe to the supported device-orientation event, if available.
30
+ *
31
+ * @since leaflet-rotate (v0.1.0)
32
+ */
20
33
  addHooks() {
21
34
  if (this._map._rotate && this.__deviceOrientationEvent) {
22
35
  L.DomEvent.on(window, this.__deviceOrientationEvent, this._throttled, this);
@@ -27,6 +40,11 @@ L.Map.CompassBearing = L.Handler.extend({
27
40
  }
28
41
  },
29
42
 
43
+ /**
44
+ * Unsubscribe from device orientation when the handler is disabled.
45
+ *
46
+ * @since leaflet-rotate (v0.1.0)
47
+ */
30
48
  removeHooks() {
31
49
  if (this._map._rotate && this.__deviceOrientationEvent) {
32
50
  L.DomEvent.off(window, this.__deviceOrientationEvent, this._throttled, this);
@@ -34,6 +52,8 @@ L.Map.CompassBearing = L.Handler.extend({
34
52
  },
35
53
 
36
54
  /**
55
+ * Apply a device orientation reading to the map bearing.
56
+ *
37
57
  * `DeviceOrientationEvent.absolute` - Indicates whether the device is providing absolute
38
58
  * orientation values (relatives to Magnetic North) or
39
59
  * using some arbitrary frame determined by the device.
@@ -50,6 +70,10 @@ L.Map.CompassBearing = L.Handler.extend({
50
70
  * @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/absolute
51
71
  * @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/alpha
52
72
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation
73
+ *
74
+ * @param {DeviceOrientationEvent} e Orientation reading from the browser.
75
+ *
76
+ * @since leaflet-rotate (v0.1.0)
53
77
  */
54
78
  _onDeviceOrientation(e) {
55
79
  let angle = e.webkitCompassHeading != null ? e.webkitCompassHeading : e.alpha;
@@ -76,5 +100,7 @@ L.Map.CompassBearing = L.Handler.extend({
76
100
  * Add Compass bearing handler to L.Map (disabled unless `window.DeviceOrientationEvent` is set).
77
101
  *
78
102
  * @property {L.Map.CompassBearing} compassBearing
103
+ *
104
+ * @since leaflet-rotate (v0.1.0)
79
105
  */
80
106
  L.Map.addInitHook('addHandler', 'compassBearing', L.Map.CompassBearing);