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/README.md +73 -4
- package/dist/leaflet-rotate-src.js +836 -103
- package/dist/leaflet-rotate-src.js.map +2 -2
- package/dist/leaflet-rotate.js +1 -1
- package/dist/leaflet-rotate.js.map +3 -3
- package/misc/leaflet-heat-rotate.js +7 -0
- package/package.json +1 -1
- package/src/control/Rotate.js +78 -5
- package/src/dom/DomUtil.js +23 -12
- package/src/dom/Draggable.js +5 -0
- package/src/geo/LatLngBounds.js +5 -0
- package/src/geometry/Bounds.js +5 -0
- package/src/geometry/Point.js +17 -3
- package/src/index.js +3 -1
- package/src/layer/DivOverlay.js +34 -5
- package/src/layer/Popup.js +14 -3
- package/src/layer/Tooltip.js +16 -0
- package/src/layer/marker/Icon.js +12 -0
- package/src/layer/marker/Marker.js +86 -7
- package/src/layer/tile/GridLayer.js +18 -2
- package/src/layer/vector/Renderer.js +57 -6
- package/src/map/Map.js +338 -56
- package/src/map/handler/CompassBearing.js +27 -1
- package/src/map/handler/ContainerMutation.js +18 -1
- package/src/map/handler/ShiftKeyRotate.js +153 -13
- package/src/map/handler/TouchGestures.js +63 -7
- package/src/map/handler/TouchRotate.js +18 -1
- package/src/map/handler/TouchZoom.js +21 -1
- package/test/features/rotation.feature +30 -0
- package/test/index.html +2 -1
- package/test/steps/bearing.steps.js +342 -0
- package/test/support/hooks.js +4 -16
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({
|
|
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
|
-
*
|
|
27
|
-
*
|
|
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
|
-
|
|
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
|
|
46
|
-
*
|
|
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
|
|
63
|
-
*
|
|
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
|
|
83
|
-
*
|
|
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
|
|
173
|
+
* @param {L.Point|number[]} point Point in map-pane coordinates.
|
|
101
174
|
*
|
|
102
|
-
* @
|
|
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
|
-
*
|
|
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
|
-
*
|
|
233
|
+
* Return the geographic bounds of the current viewport, accounting for rotation and pitch.
|
|
155
234
|
*
|
|
156
|
-
*
|
|
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
|
-
* @
|
|
237
|
+
* @returns {L.LatLngBounds} Geographic bounds visible in the map viewport.
|
|
160
238
|
*
|
|
161
|
-
* @
|
|
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
|
-
*
|
|
283
|
+
* Set the map bearing, optionally animating to the new value.
|
|
206
284
|
*
|
|
207
|
-
* @param {number} theta
|
|
208
|
-
* @param {Object} [options]
|
|
209
|
-
* @param {boolean} [options.animate=false]
|
|
210
|
-
* @param {number} [options.duration=0.25]
|
|
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
|
-
* @
|
|
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
|
-
|
|
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
|
-
*
|
|
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}
|
|
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
|
|
373
|
-
* @param {Object} [options={}] padding
|
|
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
|
|
423
|
-
* @param {Object} [options]
|
|
424
|
-
*
|
|
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 {
|
|
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 {
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
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.
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
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
|
-
*
|
|
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);
|