leaflet-rotate 0.3.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 +74 -5
- package/build/test.js +18 -0
- package/dist/leaflet-rotate-src.js +1081 -303
- package/dist/leaflet-rotate-src.js.map +3 -3
- package/dist/leaflet-rotate.js +1 -1
- package/dist/leaflet-rotate.js.map +3 -3
- package/lib/debug.js +84 -66
- package/lib/debug.min.js +1 -1
- package/misc/leaflet-heat-rotate.js +14 -7
- package/misc/loremIpsum.js +1 -1
- package/misc/places.js +1 -1
- package/misc/route.js +1 -1
- package/package.json +5 -5
- package/src/control/Rotate.js +98 -29
- package/src/dom/DomUtil.js +42 -47
- package/src/dom/Draggable.js +6 -1
- package/src/geo/LatLngBounds.js +5 -0
- package/src/geometry/Bounds.js +5 -0
- package/src/geometry/Point.js +33 -10
- package/src/index.js +3 -1
- package/src/layer/DivOverlay.js +43 -12
- package/src/layer/Popup.js +29 -16
- package/src/layer/Tooltip.js +21 -5
- package/src/layer/marker/Icon.js +20 -8
- package/src/layer/marker/Marker.js +103 -24
- package/src/layer/tile/GridLayer.js +22 -6
- package/src/layer/vector/Renderer.js +102 -11
- package/src/map/Map.js +408 -121
- package/src/map/handler/CompassBearing.js +33 -7
- package/src/map/handler/ContainerMutation.js +20 -3
- package/src/map/handler/ShiftKeyRotate.js +156 -16
- package/src/map/handler/TouchGestures.js +91 -29
- package/src/map/handler/TouchRotate.js +20 -3
- package/src/map/handler/TouchZoom.js +25 -5
- package/test/features/example.feature +5 -0
- package/test/features/rotation.feature +30 -0
- package/test/index.html +60 -22
- package/test/steps/bearing.steps.js +345 -3
- package/test/steps/example.steps.js +72 -11
- package/test/steps/gestures.steps.js +4 -4
- package/test/steps/overlays.steps.js +32 -32
- package/test/support/hooks.js +38 -2
|
@@ -3,6 +3,10 @@ var leafletRotate = (() => {
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
6
10
|
var __copyProps = (to, from, except, desc) => {
|
|
7
11
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
12
|
for (let key of __getOwnPropNames(from))
|
|
@@ -15,84 +19,92 @@ var leafletRotate = (() => {
|
|
|
15
19
|
|
|
16
20
|
// src/index.js
|
|
17
21
|
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
toPoint: () => toPoint
|
|
24
|
+
});
|
|
18
25
|
|
|
19
26
|
// src/dom/DomUtil.js
|
|
20
|
-
var domUtilProto =
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Resets the 3D CSS transform of `el` so it is
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* @param {
|
|
29
|
-
* @param {
|
|
30
|
-
* @param {
|
|
31
|
-
*
|
|
32
|
-
* @
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
var domUtilProto = Object.assign({}, L.DomUtil);
|
|
28
|
+
Object.assign(L.DomUtil, {
|
|
29
|
+
/**
|
|
30
|
+
* Resets the 3D CSS transform of `el` so it is translated by `offset` pixels,
|
|
31
|
+
* optionally scaled by `scale`, and rotated around `pivot` when `bearing` is set.
|
|
32
|
+
*
|
|
33
|
+
* @param {HTMLElement} el Element to transform.
|
|
34
|
+
* @param {L.Point} [offset] Translation in pixels; defaults to the origin.
|
|
35
|
+
* @param {number} [scale] Optional scale factor.
|
|
36
|
+
* @param {number} [bearing] Optional rotation in radians.
|
|
37
|
+
* @param {L.Point} [pivot] Point in pixels around which to rotate.
|
|
38
|
+
*
|
|
39
|
+
* @returns {void}
|
|
40
|
+
*
|
|
41
|
+
* @since leaflet-rotate (v0.1.0)
|
|
42
|
+
*/
|
|
43
|
+
setTransform(el, offset, scale, bearing, pivot) {
|
|
44
|
+
let pos = offset || new L.Point(0, 0);
|
|
36
45
|
if (!bearing) {
|
|
37
|
-
|
|
38
|
-
return domUtilProto.setTransform.apply(this, arguments);
|
|
46
|
+
return domUtilProto.setTransform(el, pos._round(), scale);
|
|
39
47
|
}
|
|
40
48
|
pos = pos.rotateFrom(bearing, pivot);
|
|
41
|
-
el.style
|
|
49
|
+
el.style.transform = "translate3d(" + pos.x + "px," + pos.y + "px,0)" + (scale ? " scale(" + scale + ")" : "") + " rotate(" + bearing + "rad)";
|
|
42
50
|
},
|
|
43
51
|
/**
|
|
44
|
-
* Sets the position of `el` to
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* to position
|
|
52
|
+
* Sets the position of `el` to `point`, using CSS transforms or top/left positioning
|
|
53
|
+
* depending on browser support, and optionally rotating it around `pivot`.
|
|
54
|
+
*
|
|
55
|
+
* @param {HTMLElement} el Element to position.
|
|
56
|
+
* @param {L.Point} point Position in pixels.
|
|
57
|
+
* @param {number} [bearing] Rotation in radians; zero or omitted uses Leaflet positioning.
|
|
58
|
+
* @param {L.Point} [pivot] Point in pixels around which to rotate.
|
|
59
|
+
* @param {number} [scale] Optional scale factor.
|
|
60
|
+
*
|
|
61
|
+
* @returns {void}
|
|
48
62
|
*
|
|
49
|
-
* @
|
|
50
|
-
* @param {L.Point} point
|
|
51
|
-
* @param {Number} bearing
|
|
52
|
-
* @param {L.Point} pivot
|
|
53
|
-
* @param {Number} scale
|
|
63
|
+
* @since leaflet-rotate (v0.1.0)
|
|
54
64
|
*/
|
|
55
|
-
setPosition
|
|
65
|
+
setPosition(el, point, bearing, pivot, scale) {
|
|
56
66
|
if (!bearing) {
|
|
57
|
-
return domUtilProto.setPosition
|
|
67
|
+
return domUtilProto.setPosition(el, point);
|
|
58
68
|
}
|
|
59
69
|
el._leaflet_pos = point;
|
|
60
|
-
|
|
61
|
-
L.DomUtil.setTransform(el, point, scale, bearing, pivot);
|
|
62
|
-
} else {
|
|
63
|
-
el.style.left = point.x + "px";
|
|
64
|
-
el.style.top = point.y + "px";
|
|
65
|
-
}
|
|
70
|
+
L.DomUtil.setTransform(el, point, scale, bearing, pivot);
|
|
66
71
|
},
|
|
67
72
|
/**
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
* @
|
|
73
|
+
* Returns the position assigned to `el`.
|
|
74
|
+
*
|
|
75
|
+
* @param {HTMLElement} el Element whose Leaflet position is requested.
|
|
76
|
+
*
|
|
77
|
+
* @returns {L.Point} Stored Leaflet position, or the position measured by Leaflet.
|
|
78
|
+
*
|
|
79
|
+
* @since leaflet-rotate (v0.1.0)
|
|
73
80
|
*/
|
|
74
|
-
|
|
81
|
+
getPosition(el) {
|
|
82
|
+
return el._leaflet_pos || domUtilProto.getPosition(el);
|
|
83
|
+
}
|
|
75
84
|
});
|
|
76
85
|
|
|
77
86
|
// src/dom/Draggable.js
|
|
78
87
|
L.Draggable.include({
|
|
79
88
|
/** @TODO */
|
|
80
|
-
// updateMapBearing
|
|
89
|
+
// updateMapBearing(mapBearing) {
|
|
81
90
|
// this._mapBearing = mapBearing;
|
|
82
91
|
// },
|
|
83
92
|
});
|
|
84
93
|
|
|
85
94
|
// src/geometry/Point.js
|
|
86
|
-
|
|
95
|
+
Object.assign(L.Point.prototype, {
|
|
87
96
|
/**
|
|
88
97
|
* Rotate around (0,0) by applying the 2D rotation matrix:
|
|
89
98
|
*
|
|
90
99
|
* ⎡ x' ⎤ = ⎡ cos θ -sin θ ⎤ ⎡ x ⎤
|
|
91
100
|
* ⎣ y' ⎦ ⎣ sin θ cos θ ⎦ ⎣ y ⎦
|
|
92
101
|
*
|
|
93
|
-
* @param theta
|
|
102
|
+
* @param {number} theta Rotation angle in radians, counterclockwise.
|
|
103
|
+
* @returns {L.Point} Rotated point (or this point when theta is zero).
|
|
104
|
+
*
|
|
105
|
+
* @since leaflet-rotate (v0.1.0)
|
|
94
106
|
*/
|
|
95
|
-
rotate
|
|
107
|
+
rotate(theta) {
|
|
96
108
|
return this.rotateFrom(theta, new L.Point(0, 0));
|
|
97
109
|
},
|
|
98
110
|
/**
|
|
@@ -104,53 +116,85 @@ var leafletRotate = (() => {
|
|
|
104
116
|
*
|
|
105
117
|
* same as `this.subtract(pivot).rotate(theta).add(pivot)`
|
|
106
118
|
*
|
|
107
|
-
* @param {
|
|
108
|
-
* @param {L.Point} pivot
|
|
119
|
+
* @param {number} theta Rotation angle in radians, counterclockwise.
|
|
120
|
+
* @param {L.Point} pivot Point to rotate around.
|
|
109
121
|
*
|
|
110
122
|
* @returns {L.Point}
|
|
123
|
+
*
|
|
124
|
+
* @since leaflet-rotate (v0.1.0)
|
|
111
125
|
*/
|
|
112
|
-
rotateFrom
|
|
126
|
+
rotateFrom(theta, pivot) {
|
|
113
127
|
if (!theta) {
|
|
114
128
|
return this;
|
|
115
129
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
const sinTheta = Math.sin(theta);
|
|
131
|
+
const cosTheta = Math.cos(theta);
|
|
132
|
+
const cx = pivot.x, cy = pivot.y;
|
|
133
|
+
const x = this.x - cx, y = this.y - cy;
|
|
120
134
|
return new L.Point(
|
|
121
135
|
x * cosTheta - y * sinTheta + cx,
|
|
122
136
|
x * sinTheta + y * cosTheta + cy
|
|
123
137
|
);
|
|
124
138
|
}
|
|
125
139
|
});
|
|
140
|
+
function toPoint(point) {
|
|
141
|
+
if (point == null || point instanceof L.Point) {
|
|
142
|
+
return point;
|
|
143
|
+
}
|
|
144
|
+
return Array.isArray(point) ? new L.Point(point[0], point[1]) : new L.Point(point.x, point.y);
|
|
145
|
+
}
|
|
126
146
|
|
|
127
147
|
// src/layer/DivOverlay.js
|
|
128
|
-
var divOverlayProto =
|
|
148
|
+
var divOverlayProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.DivOverlay.prototype));
|
|
129
149
|
L.DivOverlay.include({
|
|
130
150
|
/**
|
|
131
|
-
*
|
|
132
|
-
* the map is moved by calling `map.setBearing(theta)`
|
|
151
|
+
* Extend overlay event subscriptions to refresh position after map rotation and pitch.
|
|
133
152
|
*
|
|
134
153
|
* @listens L.Map~rotate
|
|
154
|
+
* @listens L.Map~pitch
|
|
155
|
+
*
|
|
156
|
+
* @returns {Object} Leaflet event map for the overlay.
|
|
157
|
+
*
|
|
158
|
+
* @since leaflet-rotate (v0.1.0)
|
|
159
|
+
*/
|
|
160
|
+
getEvents() {
|
|
161
|
+
const events = Object.assign(divOverlayProto.getEvents.apply(this, arguments), {
|
|
162
|
+
rotate: this._updatePosition,
|
|
163
|
+
pitch: this._updatePitchEvents
|
|
164
|
+
});
|
|
165
|
+
if (this._map && this._map._pitch) {
|
|
166
|
+
events.move = this._updatePosition;
|
|
167
|
+
}
|
|
168
|
+
return events;
|
|
169
|
+
},
|
|
170
|
+
/**
|
|
171
|
+
* Listen to map movement only while pitch is non-zero.
|
|
172
|
+
*
|
|
173
|
+
* @since leaflet-rotate (0.5.0)
|
|
135
174
|
*/
|
|
136
|
-
|
|
137
|
-
|
|
175
|
+
_updatePitchEvents() {
|
|
176
|
+
if (this._map._pitch) {
|
|
177
|
+
this._map.on("move", this._updatePosition, this);
|
|
178
|
+
} else {
|
|
179
|
+
this._map.off("move", this._updatePosition, this);
|
|
180
|
+
}
|
|
138
181
|
},
|
|
139
182
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
183
|
+
* Reposition the overlay after map movement, applying rotation/pitch-aware coordinates.
|
|
184
|
+
*
|
|
185
|
+
* @since leaflet-rotate (v0.1.0)
|
|
142
186
|
*/
|
|
143
|
-
_updatePosition
|
|
187
|
+
_updatePosition() {
|
|
144
188
|
if (!this._map) {
|
|
145
189
|
return;
|
|
146
190
|
}
|
|
147
191
|
divOverlayProto._updatePosition.apply(this, arguments);
|
|
148
192
|
if (this._map._rotate && this._zoomAnimated) {
|
|
149
|
-
|
|
150
|
-
|
|
193
|
+
const anchor = this._getAnchor();
|
|
194
|
+
const pos = L.DomUtil.getPosition(this._container).subtract(anchor);
|
|
151
195
|
L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
|
|
152
196
|
} else if (this._map._rotate) {
|
|
153
|
-
|
|
197
|
+
const mapPanePoint = this._map.rotatedPointToMapPanePoint(this._map.latLngToLayerPoint(this._latlng)), offset = toPoint(this.options.offset || [0, 0]), anchor = this._getAnchor();
|
|
154
198
|
this._containerLeft = -Math.round(this._containerWidth / 2) + mapPanePoint.x + offset.x + anchor.x;
|
|
155
199
|
this._containerBottom = -(mapPanePoint.y + offset.y + anchor.y);
|
|
156
200
|
this._container.style.left = this._containerLeft + "px";
|
|
@@ -163,26 +207,33 @@ var leafletRotate = (() => {
|
|
|
163
207
|
});
|
|
164
208
|
|
|
165
209
|
// src/layer/Popup.js
|
|
166
|
-
var popupProto =
|
|
210
|
+
var popupProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Popup.prototype));
|
|
167
211
|
L.Popup.include({
|
|
168
212
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
213
|
+
* Update the popup position during zoom animation and transform it into map-pane coordinates.
|
|
214
|
+
*
|
|
215
|
+
* @param {Object} e Zoom animation event.
|
|
216
|
+
*
|
|
217
|
+
* @since leaflet-rotate (v0.1.0)
|
|
171
218
|
*/
|
|
172
|
-
_animateZoom
|
|
219
|
+
_animateZoom(e) {
|
|
173
220
|
popupProto._animateZoom.apply(this, arguments);
|
|
174
221
|
if (this._map && this._map._rotate) {
|
|
175
|
-
|
|
176
|
-
|
|
222
|
+
const anchor = this._getAnchor();
|
|
223
|
+
const pos = L.DomUtil.getPosition(this._container).subtract(anchor);
|
|
177
224
|
L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
|
|
178
225
|
}
|
|
179
226
|
},
|
|
180
227
|
/**
|
|
181
|
-
*
|
|
228
|
+
* Pan the map when a popup with auto-pan enabled would extend beyond the viewport.
|
|
182
229
|
*
|
|
183
230
|
* @see https://github.com/fnicollet/Leaflet/pull/21
|
|
231
|
+
*
|
|
232
|
+
* @fires L.Map#autopanstart
|
|
233
|
+
*
|
|
234
|
+
* @since leaflet-rotate (v0.1.0)
|
|
184
235
|
*/
|
|
185
|
-
_adjustPan
|
|
236
|
+
_adjustPan() {
|
|
186
237
|
if (!this.options.autoPan || this._map._panAnim && this._map._panAnim._inProgress) {
|
|
187
238
|
return;
|
|
188
239
|
}
|
|
@@ -190,9 +241,10 @@ var leafletRotate = (() => {
|
|
|
190
241
|
this._autopanning = false;
|
|
191
242
|
return;
|
|
192
243
|
}
|
|
193
|
-
|
|
244
|
+
const map = this._map, marginBottom = parseInt(getComputedStyle(this._container).marginBottom, 10) || 0, containerHeight = this._container.offsetHeight + marginBottom, containerWidth = this._containerWidth, layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
|
|
194
245
|
layerPos._add(L.DomUtil.getPosition(this._container));
|
|
195
|
-
|
|
246
|
+
const containerPos = layerPos._add(this._map._getMapPanePos()), padding = toPoint(this.options.autoPanPadding || [0, 0]), paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding), paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding), size = map.getSize();
|
|
247
|
+
let dx = 0, dy = 0;
|
|
196
248
|
if (containerPos.x + containerWidth + paddingBR.x > size.x) {
|
|
197
249
|
dx = containerPos.x + containerWidth - size.x + paddingBR.x;
|
|
198
250
|
}
|
|
@@ -215,41 +267,61 @@ var leafletRotate = (() => {
|
|
|
215
267
|
});
|
|
216
268
|
|
|
217
269
|
// src/layer/Tooltip.js
|
|
218
|
-
var tooltipProto =
|
|
270
|
+
var tooltipProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Tooltip.prototype));
|
|
219
271
|
L.Tooltip.include({
|
|
220
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Reposition the tooltip during zoom animation using transformed map coordinates.
|
|
274
|
+
*
|
|
275
|
+
* @param {Object} e Zoom animation event.
|
|
276
|
+
*
|
|
277
|
+
* @since leaflet-rotate (v0.1.0)
|
|
278
|
+
*/
|
|
279
|
+
_animateZoom(e) {
|
|
221
280
|
if (!this._map._rotate) {
|
|
222
281
|
return tooltipProto._animateZoom.apply(this, arguments);
|
|
223
282
|
}
|
|
224
|
-
|
|
283
|
+
let pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
|
|
225
284
|
pos = this._map.rotatedPointToMapPanePoint(pos);
|
|
226
285
|
this._setPosition(pos);
|
|
227
286
|
},
|
|
228
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Recompute tooltip screen position after map movement.
|
|
289
|
+
*
|
|
290
|
+
* @since leaflet-rotate (v0.1.0)
|
|
291
|
+
*/
|
|
292
|
+
_updatePosition() {
|
|
229
293
|
if (!this._map._rotate) {
|
|
230
294
|
return tooltipProto._updatePosition.apply(this, arguments);
|
|
231
295
|
}
|
|
232
|
-
|
|
296
|
+
let pos = this._map.latLngToLayerPoint(this._latlng);
|
|
233
297
|
pos = this._map.rotatedPointToMapPanePoint(pos);
|
|
234
298
|
this._setPosition(pos);
|
|
235
299
|
}
|
|
236
300
|
});
|
|
237
301
|
|
|
238
302
|
// src/layer/marker/Icon.js
|
|
239
|
-
var iconProto =
|
|
303
|
+
var iconProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Icon.prototype));
|
|
240
304
|
L.Icon.include({
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
305
|
+
/**
|
|
306
|
+
* Set marker icon dimensions and anchor styles, including the rotation origin.
|
|
307
|
+
*
|
|
308
|
+
* @param {HTMLImageElement} img Icon or shadow element.
|
|
309
|
+
* @param {string} name Icon style key (`icon` or `shadow`).
|
|
310
|
+
*
|
|
311
|
+
* @since leaflet-rotate (v0.1.0)
|
|
312
|
+
*/
|
|
313
|
+
_setIconStyles(img, name) {
|
|
314
|
+
const options = this.options;
|
|
315
|
+
let sizeOption = options[name + "Size"];
|
|
244
316
|
if (typeof sizeOption === "number") {
|
|
245
317
|
sizeOption = [sizeOption, sizeOption];
|
|
246
318
|
}
|
|
247
|
-
|
|
319
|
+
const size = sizeOption ? new L.Point(...Array.isArray(sizeOption) ? sizeOption : [sizeOption.x, sizeOption.y]) : null, anchorOption = name === "shadow" && options.shadowAnchor || options.iconAnchor || size && size.divideBy(2, true), anchor = anchorOption ? new L.Point(...Array.isArray(anchorOption) ? anchorOption : [anchorOption.x, anchorOption.y]) : null;
|
|
248
320
|
img.className = "leaflet-marker-" + name + " " + (options.className || "");
|
|
249
321
|
if (anchor) {
|
|
250
322
|
img.style.marginLeft = -anchor.x + "px";
|
|
251
323
|
img.style.marginTop = -anchor.y + "px";
|
|
252
|
-
img.style
|
|
324
|
+
img.style.transformOrigin = anchor.x + "px " + anchor.y + "px 0px";
|
|
253
325
|
}
|
|
254
326
|
if (size) {
|
|
255
327
|
img.style.width = size.x + "px";
|
|
@@ -259,50 +331,69 @@ var leafletRotate = (() => {
|
|
|
259
331
|
});
|
|
260
332
|
|
|
261
333
|
// src/layer/marker/Marker.js
|
|
262
|
-
var markerProto =
|
|
334
|
+
var markerProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Marker.prototype));
|
|
263
335
|
L.Marker.mergeOptions({
|
|
264
336
|
/**
|
|
265
|
-
* Rotation of
|
|
337
|
+
* Rotation of the marker icon in radians.
|
|
266
338
|
*
|
|
267
339
|
* @type {Number}
|
|
340
|
+
*
|
|
341
|
+
* @since leaflet-rotate (v0.1.0)
|
|
268
342
|
*/
|
|
269
343
|
rotation: 0,
|
|
270
344
|
/**
|
|
271
|
-
* Rotate
|
|
345
|
+
* Rotate the marker icon when the map bearing changes.
|
|
346
|
+
*
|
|
347
|
+
* Combine with `keepUpright: true` to keep the marker readable while
|
|
348
|
+
* preserving its line alignment.
|
|
272
349
|
*
|
|
273
350
|
* @type {Boolean}
|
|
351
|
+
*
|
|
352
|
+
* @since leaflet-rotate (v0.1.0)
|
|
274
353
|
*/
|
|
275
354
|
rotateWithView: false,
|
|
276
355
|
/**
|
|
277
|
-
* Flip
|
|
356
|
+
* Flip the icon when needed to keep it upright while map-aligned.
|
|
278
357
|
*
|
|
279
358
|
* @type {Boolean}
|
|
359
|
+
*
|
|
360
|
+
* @since leaflet-rotate (v0.1.0)
|
|
280
361
|
*/
|
|
281
362
|
keepUpright: false,
|
|
282
363
|
/**
|
|
283
|
-
*
|
|
364
|
+
* Optional scale of the marker icon and shadow.
|
|
284
365
|
*
|
|
285
366
|
* @type {Number}
|
|
367
|
+
*
|
|
368
|
+
* @since leaflet-rotate (v0.1.0)
|
|
286
369
|
*/
|
|
287
370
|
scale: void 0
|
|
288
371
|
});
|
|
289
372
|
var markerDragProto;
|
|
290
373
|
var MarkerDrag = {
|
|
291
|
-
// _onDragStart
|
|
374
|
+
// _onDragStart() {
|
|
292
375
|
// if (!this._marker._map._rotate) {
|
|
293
376
|
// return markerDragProto._onDragStart.apply(this, arguments);
|
|
294
377
|
// }
|
|
295
378
|
// this._draggable.updateMapBearing(this._marker._map._bearing);
|
|
296
379
|
// },
|
|
297
|
-
|
|
298
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Update a dragged marker's geographic position from its transformed icon position.
|
|
382
|
+
*
|
|
383
|
+
* @param {Object} e Marker drag event with `latlng` and `oldLatLng` fields.
|
|
384
|
+
*
|
|
385
|
+
* @since leaflet-rotate (v0.1.0)
|
|
386
|
+
*/
|
|
387
|
+
_onDrag(e) {
|
|
388
|
+
const marker = this._marker, rotated_marker = marker.options.rotation || marker.options.rotateWithView, shadow = marker._shadow;
|
|
389
|
+
let iconPos = L.DomUtil.getPosition(marker._icon);
|
|
299
390
|
if (!rotated_marker && shadow) {
|
|
300
391
|
L.DomUtil.setPosition(shadow, iconPos);
|
|
301
392
|
}
|
|
302
393
|
if (marker._map._rotate) {
|
|
303
394
|
iconPos = marker._map.mapPanePointToRotatedPoint(iconPos);
|
|
304
395
|
}
|
|
305
|
-
|
|
396
|
+
const latlng = marker._map.layerPointToLatLng(iconPos);
|
|
306
397
|
marker._latlng = latlng;
|
|
307
398
|
e.latlng = latlng;
|
|
308
399
|
e.oldLatLng = this._oldLatLng;
|
|
@@ -310,7 +401,14 @@ var leafletRotate = (() => {
|
|
|
310
401
|
else marker.fire("move", e);
|
|
311
402
|
marker.fire("drag", e);
|
|
312
403
|
},
|
|
313
|
-
|
|
404
|
+
/**
|
|
405
|
+
* Reposition the marker after a drag ends, then run Leaflet's drag-end logic.
|
|
406
|
+
*
|
|
407
|
+
* @param {Object} e Marker drag-end event.
|
|
408
|
+
*
|
|
409
|
+
* @since leaflet-rotate (v0.1.0)
|
|
410
|
+
*/
|
|
411
|
+
_onDragEnd(e) {
|
|
314
412
|
if (this._marker._map._rotate) {
|
|
315
413
|
this._marker.update();
|
|
316
414
|
}
|
|
@@ -319,16 +417,44 @@ var leafletRotate = (() => {
|
|
|
319
417
|
};
|
|
320
418
|
L.Marker.include({
|
|
321
419
|
/**
|
|
322
|
-
*
|
|
323
|
-
* is moved by calling `map.setBearing(theta)`
|
|
420
|
+
* Extend marker event subscriptions to reposition its icon after bearing or pitch changes.
|
|
324
421
|
*
|
|
325
422
|
* @listens L.Map~rotate
|
|
423
|
+
* @listens L.Map~pitch
|
|
424
|
+
*
|
|
425
|
+
* @returns {Object} Leaflet event map for the marker.
|
|
426
|
+
*
|
|
427
|
+
* @since leaflet-rotate (v0.1.0)
|
|
326
428
|
*/
|
|
327
|
-
getEvents
|
|
328
|
-
|
|
429
|
+
getEvents() {
|
|
430
|
+
const events = Object.assign(markerProto.getEvents.apply(this, arguments), {
|
|
431
|
+
rotate: this.update,
|
|
432
|
+
pitch: this._updatePitchEvents
|
|
433
|
+
});
|
|
434
|
+
if (this._map && this._map._pitch) {
|
|
435
|
+
events.move = this.update;
|
|
436
|
+
}
|
|
437
|
+
return events;
|
|
438
|
+
},
|
|
439
|
+
/**
|
|
440
|
+
* Subscribe to map movement while pitch is active to keep the icon aligned.
|
|
441
|
+
*
|
|
442
|
+
* @since leaflet-rotate (0.5.0)
|
|
443
|
+
*/
|
|
444
|
+
_updatePitchEvents() {
|
|
445
|
+
if (this._map._pitch) {
|
|
446
|
+
this._map.on("move", this.update, this);
|
|
447
|
+
} else {
|
|
448
|
+
this._map.off("move", this.update, this);
|
|
449
|
+
}
|
|
329
450
|
},
|
|
330
|
-
|
|
331
|
-
|
|
451
|
+
/**
|
|
452
|
+
* Install rotation-aware drag callbacks when marker dragging is enabled.
|
|
453
|
+
*
|
|
454
|
+
* @since leaflet-rotate (v0.1.0)
|
|
455
|
+
*/
|
|
456
|
+
_initInteraction() {
|
|
457
|
+
const ret = markerProto._initInteraction.apply(this, arguments);
|
|
332
458
|
if (this.dragging && this.dragging.enabled() && this._map && this._map._rotate) {
|
|
333
459
|
markerDragProto = markerDragProto || Object.getPrototypeOf(this.dragging);
|
|
334
460
|
this.dragging.disable();
|
|
@@ -341,11 +467,18 @@ var leafletRotate = (() => {
|
|
|
341
467
|
}
|
|
342
468
|
return ret;
|
|
343
469
|
},
|
|
344
|
-
|
|
470
|
+
/**
|
|
471
|
+
* Position and rotate the marker icon and shadow in map-pane coordinates.
|
|
472
|
+
*
|
|
473
|
+
* @param {L.Point} pos Marker anchor in layer-point coordinates.
|
|
474
|
+
*
|
|
475
|
+
* @since leaflet-rotate (v0.1.0)
|
|
476
|
+
*/
|
|
477
|
+
_setPos(pos) {
|
|
345
478
|
if (this._map._rotate) {
|
|
346
479
|
pos = this._map.rotatedPointToMapPanePoint(pos);
|
|
347
480
|
}
|
|
348
|
-
|
|
481
|
+
let bearing = this.options.rotation || 0;
|
|
349
482
|
if (this.options.rotateWithView) {
|
|
350
483
|
bearing += this._map._bearing;
|
|
351
484
|
}
|
|
@@ -365,29 +498,41 @@ var leafletRotate = (() => {
|
|
|
365
498
|
this._zIndex = pos.y + this.options.zIndexOffset;
|
|
366
499
|
this._resetZIndex();
|
|
367
500
|
},
|
|
368
|
-
// _updateZIndex
|
|
501
|
+
// _updateZIndex(offset) {
|
|
369
502
|
// if (!this._map._rotate) {
|
|
370
503
|
// return markerProto._updateZIndex.apply(this, arguments);
|
|
371
504
|
// }
|
|
372
505
|
// this._icon.style.zIndex = Math.round(this._zIndex + offset);
|
|
373
506
|
// },
|
|
374
|
-
|
|
507
|
+
/**
|
|
508
|
+
* Set the marker icon's rotation and redraw it.
|
|
509
|
+
*
|
|
510
|
+
* @param {number} rotation Angle in radians.
|
|
511
|
+
*
|
|
512
|
+
* @returns {void}
|
|
513
|
+
*
|
|
514
|
+
* @since leaflet-rotate (v0.1.0)
|
|
515
|
+
*/
|
|
516
|
+
setRotation(rotation) {
|
|
375
517
|
this.options.rotation = rotation;
|
|
376
518
|
this.update();
|
|
377
519
|
}
|
|
378
520
|
});
|
|
379
521
|
|
|
380
522
|
// src/layer/tile/GridLayer.js
|
|
381
|
-
var gridLayerProto =
|
|
523
|
+
var gridLayerProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.GridLayer.prototype));
|
|
382
524
|
L.GridLayer.include({
|
|
383
525
|
/**
|
|
384
|
-
*
|
|
385
|
-
* moved by just calling `map.setBearing(theta)`
|
|
526
|
+
* Add a throttled tile refresh after map bearing changes.
|
|
386
527
|
*
|
|
387
528
|
* @listens L.Map~rotate
|
|
529
|
+
*
|
|
530
|
+
* @returns {Object} Leaflet event map for the grid layer.
|
|
531
|
+
*
|
|
532
|
+
* @since leaflet-rotate (v0.1.0)
|
|
388
533
|
*/
|
|
389
|
-
getEvents
|
|
390
|
-
|
|
534
|
+
getEvents() {
|
|
535
|
+
const events = gridLayerProto.getEvents.apply(this, arguments);
|
|
391
536
|
if (this._map._rotate && !this.options.updateWhenIdle) {
|
|
392
537
|
if (!this._onRotate) {
|
|
393
538
|
this._onRotate = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
|
|
@@ -396,7 +541,16 @@ var leafletRotate = (() => {
|
|
|
396
541
|
}
|
|
397
542
|
return events;
|
|
398
543
|
},
|
|
399
|
-
|
|
544
|
+
/**
|
|
545
|
+
* Return tile pixel bounds that cover the rotated viewport.
|
|
546
|
+
*
|
|
547
|
+
* @param {L.LatLng} center View center used to calculate bounds.
|
|
548
|
+
*
|
|
549
|
+
* @returns {L.Bounds} Pixel bounds covered by the layer's tiles.
|
|
550
|
+
*
|
|
551
|
+
* @since leaflet-rotate (v0.1.0)
|
|
552
|
+
*/
|
|
553
|
+
_getTiledPixelBounds(center) {
|
|
400
554
|
if (!this._map._rotate) {
|
|
401
555
|
return gridLayerProto._getTiledPixelBounds.apply(this, arguments);
|
|
402
556
|
}
|
|
@@ -405,29 +559,58 @@ var leafletRotate = (() => {
|
|
|
405
559
|
});
|
|
406
560
|
|
|
407
561
|
// src/layer/vector/Renderer.js
|
|
408
|
-
var rendererProto =
|
|
562
|
+
var rendererProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Renderer.prototype));
|
|
563
|
+
var rendererBase = Object.getPrototypeOf(L.Renderer.prototype);
|
|
564
|
+
var rendererGetEvents = rendererProto.getEvents || rendererBase.getEvents;
|
|
565
|
+
var rendererOnMoveEnd = rendererProto._onMoveEnd || rendererBase._onMoveEnd;
|
|
409
566
|
L.Renderer.include({
|
|
410
567
|
/**
|
|
411
|
-
*
|
|
412
|
-
* map is moved by just calling `map.setBearing(theta)`
|
|
568
|
+
* Subscribe the renderer to map rotation so its visible bounds are recalculated.
|
|
413
569
|
*
|
|
414
570
|
* @listens L.Map~rotate
|
|
571
|
+
*
|
|
572
|
+
* @returns {Object} Leaflet event map for the renderer.
|
|
573
|
+
*
|
|
574
|
+
* @since leaflet-rotate (v0.1.0)
|
|
575
|
+
*/
|
|
576
|
+
getEvents() {
|
|
577
|
+
const events = rendererGetEvents.apply(this, arguments);
|
|
578
|
+
return Object.assign(events, { rotate: this._onRotate });
|
|
579
|
+
},
|
|
580
|
+
/**
|
|
581
|
+
* Reproject vector paths and refresh renderer bounds after bearing changes.
|
|
582
|
+
*
|
|
583
|
+
* @since leaflet-rotate (v0.1.0)
|
|
415
584
|
*/
|
|
416
|
-
|
|
417
|
-
|
|
585
|
+
_onRotate() {
|
|
586
|
+
if (!this._map._rotate) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
this._onZoomEnd();
|
|
590
|
+
if (L.version.startsWith("2.")) {
|
|
591
|
+
this._onMoveEnd();
|
|
592
|
+
} else {
|
|
593
|
+
this._update();
|
|
594
|
+
}
|
|
418
595
|
},
|
|
419
596
|
/**
|
|
420
|
-
*
|
|
597
|
+
* Add the zoom-animation class needed for correct renderer transforms in Leaflet 1.
|
|
598
|
+
*
|
|
599
|
+
* Fixes `map.flyTo()` when `map.options.zoomAnimation` is false.
|
|
421
600
|
*
|
|
422
601
|
* @see https://github.com/Leaflet/Leaflet/pull/8794
|
|
602
|
+
*
|
|
603
|
+
* @since leaflet-rotate (v0.2.0)
|
|
423
604
|
*/
|
|
424
|
-
onAdd
|
|
605
|
+
onAdd() {
|
|
425
606
|
rendererProto.onAdd.apply(this, arguments);
|
|
426
607
|
if (L.version <= "1.9.3") {
|
|
427
608
|
this._container.classList.add("leaflet-zoom-animated");
|
|
428
609
|
}
|
|
429
610
|
},
|
|
430
611
|
/**
|
|
612
|
+
* Update the renderer transform during zoom animation, accounting for rotated coordinates.
|
|
613
|
+
*
|
|
431
614
|
* @FIXME layer drifts on `map.setZoom()` (eg. zoom during animation)
|
|
432
615
|
*
|
|
433
616
|
* the main cause seems to be related to `this._updateTransform(path._center, path._zoom))`
|
|
@@ -442,20 +625,67 @@ var leafletRotate = (() => {
|
|
|
442
625
|
* @see https://github.com/Leaflet/Leaflet/pull/8103
|
|
443
626
|
* @see https://github.com/Leaflet/Leaflet/issues/7466
|
|
444
627
|
*
|
|
445
|
-
* @TODO
|
|
628
|
+
* @TODO recheck these changes against leaflet@v1.9.3
|
|
446
629
|
*
|
|
447
630
|
* @see https://github.com/Leaflet/Leaflet/compare/v1.7.0...v1.9.3
|
|
631
|
+
*
|
|
632
|
+
* @param {L.LatLng} center Target map center.
|
|
633
|
+
* @param {number} zoom Target zoom level.
|
|
634
|
+
*
|
|
635
|
+
* @since leaflet-rotate (v0.1.0)
|
|
448
636
|
*/
|
|
449
|
-
_updateTransform
|
|
637
|
+
_updateTransform(center, zoom) {
|
|
638
|
+
if (L.version.startsWith("2.")) {
|
|
639
|
+
if (this._map._rotate) {
|
|
640
|
+
const scale2 = this._map.getZoomScale(zoom, this._zoom), offset2 = this._topLeftPoint.multiplyBy(scale2).subtract(this._map._getNewPixelOrigin(center, zoom));
|
|
641
|
+
L.DomUtil.setTransform(this._container, offset2, scale2);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
return rendererBase._updateTransform.apply(this, arguments);
|
|
645
|
+
}
|
|
450
646
|
if (!this._map._rotate) {
|
|
451
647
|
return rendererProto._updateTransform.apply(this, arguments);
|
|
452
648
|
}
|
|
453
649
|
if (zoom === this._zoom) {
|
|
454
650
|
return;
|
|
455
651
|
}
|
|
456
|
-
|
|
652
|
+
const scale = this._map.getZoomScale(zoom, this._zoom), offset = this._map._latLngToNewLayerPoint(this._topLeft, zoom, center);
|
|
457
653
|
L.DomUtil.setTransform(this._container, offset, scale);
|
|
458
654
|
},
|
|
655
|
+
/**
|
|
656
|
+
* Recalculate bounds and finish renderer updates after movement on Leaflet 2.
|
|
657
|
+
*
|
|
658
|
+
* @param {Object} ev Map movement event.
|
|
659
|
+
*
|
|
660
|
+
* @since leaflet-rotate (v0.4.0)
|
|
661
|
+
*/
|
|
662
|
+
_onMoveEnd(ev) {
|
|
663
|
+
if (!L.version.startsWith("2.") || !this._map._rotate) {
|
|
664
|
+
return rendererOnMoveEnd.apply(this, arguments);
|
|
665
|
+
}
|
|
666
|
+
this._bounds = this._map._getPaddedPixelBounds(this.options.padding);
|
|
667
|
+
this._topLeft = this._map.layerPointToLatLng(this._bounds.min);
|
|
668
|
+
this._center = this._map.getCenter();
|
|
669
|
+
this._zoom = this._map.getZoom();
|
|
670
|
+
this._topLeftPoint = this._map._getNewPixelOrigin(this._center, this._zoom).add(this._bounds.min);
|
|
671
|
+
this._updateTransform(this._center, this._zoom);
|
|
672
|
+
this._resizeContainer();
|
|
673
|
+
this._onSettled(ev);
|
|
674
|
+
},
|
|
675
|
+
/**
|
|
676
|
+
* Resize the renderer container to cover the pitch-aware map bounds.
|
|
677
|
+
*
|
|
678
|
+
* @since leaflet-rotate (v0.4.0)
|
|
679
|
+
*/
|
|
680
|
+
_resizeContainer() {
|
|
681
|
+
if (!L.version.startsWith("2.") || !this._map._rotate || !this._bounds) {
|
|
682
|
+
return rendererBase._resizeContainer.apply(this, arguments);
|
|
683
|
+
}
|
|
684
|
+
const size = this._bounds.getSize();
|
|
685
|
+
this._container.style.width = size.x + "px";
|
|
686
|
+
this._container.style.height = size.y + "px";
|
|
687
|
+
return size;
|
|
688
|
+
},
|
|
459
689
|
// getEvents() {
|
|
460
690
|
// const events = {
|
|
461
691
|
// viewreset: this._reset,
|
|
@@ -491,7 +721,12 @@ var leafletRotate = (() => {
|
|
|
491
721
|
// this._layers[id]._update();
|
|
492
722
|
// }
|
|
493
723
|
// },
|
|
494
|
-
|
|
724
|
+
/**
|
|
725
|
+
* Recalculate renderer bounds and cached view state.
|
|
726
|
+
*
|
|
727
|
+
* @since leaflet-rotate (v0.1.0)
|
|
728
|
+
*/
|
|
729
|
+
_update() {
|
|
495
730
|
if (!this._map._rotate) {
|
|
496
731
|
return rendererProto._update.apply(this, arguments);
|
|
497
732
|
}
|
|
@@ -503,10 +738,37 @@ var leafletRotate = (() => {
|
|
|
503
738
|
});
|
|
504
739
|
|
|
505
740
|
// src/map/Map.js
|
|
506
|
-
var mapProto =
|
|
507
|
-
L.Map.mergeOptions({
|
|
741
|
+
var mapProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Map.prototype));
|
|
742
|
+
L.Map.mergeOptions({
|
|
743
|
+
/**
|
|
744
|
+
* @type {boolean} Enable bearing and pitch transformations.
|
|
745
|
+
*
|
|
746
|
+
* @since leaflet-rotate (v0.1.0)
|
|
747
|
+
*/
|
|
748
|
+
rotate: false,
|
|
749
|
+
/**
|
|
750
|
+
* @type {number} Initial map bearing in degrees.
|
|
751
|
+
*
|
|
752
|
+
* @since leaflet-rotate (v0.1.0)
|
|
753
|
+
*/
|
|
754
|
+
bearing: 0,
|
|
755
|
+
/**
|
|
756
|
+
* @type {number} Initial map pitch in degrees, clamped to 0–85.
|
|
757
|
+
*
|
|
758
|
+
* @since leaflet-rotate (0.5.0)
|
|
759
|
+
*/
|
|
760
|
+
pitch: 0
|
|
761
|
+
});
|
|
508
762
|
L.Map.include({
|
|
509
|
-
|
|
763
|
+
/**
|
|
764
|
+
* Apply an immediate animated-zoom frame without waiting for a transition delay.
|
|
765
|
+
*
|
|
766
|
+
* @param {L.LatLng} center Target center.
|
|
767
|
+
* @param {number} zoom Target zoom level.
|
|
768
|
+
*
|
|
769
|
+
* @since leaflet-rotate (v0.1.0)
|
|
770
|
+
*/
|
|
771
|
+
_animateZoomNoDelay(center, zoom) {
|
|
510
772
|
if (!this._mapPane) {
|
|
511
773
|
return;
|
|
512
774
|
}
|
|
@@ -517,17 +779,24 @@ var leafletRotate = (() => {
|
|
|
517
779
|
this._onZoomTransitionEnd();
|
|
518
780
|
},
|
|
519
781
|
/**
|
|
520
|
-
*
|
|
521
|
-
*
|
|
782
|
+
* Initialize the Leaflet map and apply its initial bearing and pitch.
|
|
783
|
+
*
|
|
784
|
+
* @param {HTMLElement|string} id Map container element or its ID.
|
|
785
|
+
* @param {Object} [options={}] Leaflet map options, including `rotate`, `bearing`, and `pitch`.
|
|
786
|
+
*
|
|
787
|
+
* @since leaflet-rotate (v0.1.0)
|
|
522
788
|
*/
|
|
523
|
-
initialize
|
|
789
|
+
initialize(id, options) {
|
|
524
790
|
if (options.rotate) {
|
|
525
791
|
this._rotate = true;
|
|
526
792
|
this._bearing = 0;
|
|
793
|
+
this._pitch = 0;
|
|
527
794
|
}
|
|
528
795
|
mapProto.initialize.apply(this, arguments);
|
|
529
796
|
if (this.options.rotate) {
|
|
797
|
+
this.on("resize", this._updatePitchTransform, this);
|
|
530
798
|
this.setBearing(this.options.bearing);
|
|
799
|
+
this.setPitch(this.options.pitch);
|
|
531
800
|
}
|
|
532
801
|
},
|
|
533
802
|
/**
|
|
@@ -535,27 +804,42 @@ var leafletRotate = (() => {
|
|
|
535
804
|
* returns the corresponding pixel coordinate relative to
|
|
536
805
|
* the [origin pixel](#map-getpixelorigin).
|
|
537
806
|
*
|
|
538
|
-
* @param {L.Point} point
|
|
539
|
-
*
|
|
807
|
+
* @param {L.Point|number[]} point Pixel coordinate in the map container.
|
|
808
|
+
*
|
|
809
|
+
* @returns {L.Point} Corresponding layer-point coordinate.
|
|
810
|
+
*
|
|
811
|
+
* @since leaflet-rotate (v0.1.0)
|
|
540
812
|
*/
|
|
541
|
-
containerPointToLayerPoint
|
|
813
|
+
containerPointToLayerPoint(point) {
|
|
542
814
|
if (!this._rotate) {
|
|
543
815
|
return mapProto.containerPointToLayerPoint.apply(this, arguments);
|
|
544
816
|
}
|
|
545
|
-
|
|
817
|
+
if (this._pitch) {
|
|
818
|
+
return this._transformPitchPoint(
|
|
819
|
+
toPoint(point).subtract(this._getMapPanePos()),
|
|
820
|
+
this._pitchInverseTransform
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
return toPoint(point).subtract(this._getMapPanePos()).rotateFrom(-this._bearing, this._getRotatePanePos()).subtract(this._getRotatePanePos());
|
|
546
824
|
},
|
|
547
825
|
/**
|
|
548
826
|
* Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
|
|
549
827
|
* returns the corresponding pixel coordinate relative to the map container.
|
|
550
828
|
*
|
|
551
|
-
* @param {L.Point} point
|
|
552
|
-
*
|
|
829
|
+
* @param {L.Point|number[]} point Pixel coordinate in layer-point space.
|
|
830
|
+
*
|
|
831
|
+
* @returns {L.Point} Corresponding pixel coordinate in the map container.
|
|
832
|
+
*
|
|
833
|
+
* @since leaflet-rotate (v0.1.0)
|
|
553
834
|
*/
|
|
554
|
-
layerPointToContainerPoint
|
|
835
|
+
layerPointToContainerPoint(point) {
|
|
555
836
|
if (!this._rotate) {
|
|
556
837
|
return mapProto.layerPointToContainerPoint.apply(this, arguments);
|
|
557
838
|
}
|
|
558
|
-
|
|
839
|
+
if (this._pitch) {
|
|
840
|
+
return this._transformPitchPoint(point, this._pitchTransform).add(this._getMapPanePos());
|
|
841
|
+
}
|
|
842
|
+
return toPoint(point).add(this._getRotatePanePos()).rotateFrom(this._bearing, this._getRotatePanePos()).add(this._getMapPanePos());
|
|
559
843
|
},
|
|
560
844
|
/**
|
|
561
845
|
* Converts a coordinate from the rotated pane reference system
|
|
@@ -564,13 +848,17 @@ var leafletRotate = (() => {
|
|
|
564
848
|
* (rotatePane) --> (mapPane)
|
|
565
849
|
* (rotatePane) --> (norotatePane)
|
|
566
850
|
*
|
|
567
|
-
* @param {L.Point} point
|
|
568
|
-
* @returns {L.Point}
|
|
851
|
+
* @param {L.Point|number[]} point Point in the rotated pane.
|
|
569
852
|
*
|
|
570
|
-
* @
|
|
853
|
+
* @returns {L.Point} Corresponding point in map-pane coordinates.
|
|
854
|
+
*
|
|
855
|
+
* @since leaflet-rotate (v0.1.0)
|
|
571
856
|
*/
|
|
572
|
-
rotatedPointToMapPanePoint
|
|
573
|
-
|
|
857
|
+
rotatedPointToMapPanePoint(point) {
|
|
858
|
+
if (this._pitch) {
|
|
859
|
+
return this._transformPitchPoint(point, this._pitchTransform);
|
|
860
|
+
}
|
|
861
|
+
return toPoint(point).rotate(this._bearing)._add(this._getRotatePanePos());
|
|
574
862
|
},
|
|
575
863
|
/**
|
|
576
864
|
* Converts a coordinate from the not rotated map pane reference system
|
|
@@ -579,18 +867,23 @@ var leafletRotate = (() => {
|
|
|
579
867
|
* (mapPane) --> (rotatePane)
|
|
580
868
|
* (norotatePane) --> (rotatePane)
|
|
581
869
|
*
|
|
582
|
-
* @param {L.Point} point
|
|
870
|
+
* @param {L.Point|number[]} point Point in map-pane coordinates.
|
|
583
871
|
*
|
|
584
|
-
* @
|
|
872
|
+
* @returns {L.Point} Corresponding point in the rotated pane.
|
|
873
|
+
*
|
|
874
|
+
* @since leaflet-rotate (v0.1.0)
|
|
585
875
|
*/
|
|
586
|
-
mapPanePointToRotatedPoint
|
|
587
|
-
|
|
876
|
+
mapPanePointToRotatedPoint(point) {
|
|
877
|
+
if (this._pitch) {
|
|
878
|
+
return this._transformPitchPoint(point, this._pitchInverseTransform);
|
|
879
|
+
}
|
|
880
|
+
return toPoint(point)._subtract(this._getRotatePanePos()).rotate(-this._bearing);
|
|
588
881
|
},
|
|
589
|
-
// latLngToLayerPoint
|
|
590
|
-
//
|
|
882
|
+
// latLngToLayerPoint(latlng) {
|
|
883
|
+
// const projectedPoint = this.project(L.latLng(latlng))._round();
|
|
591
884
|
// return projectedPoint._subtract(this.getPixelOrigin());
|
|
592
885
|
// },
|
|
593
|
-
// latLngToContainerPoint
|
|
886
|
+
// latLngToContainerPoint(latlng) {
|
|
594
887
|
// return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
|
|
595
888
|
// },
|
|
596
889
|
/**
|
|
@@ -599,39 +892,39 @@ var leafletRotate = (() => {
|
|
|
599
892
|
*
|
|
600
893
|
* @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/map/Map.js#L1114-L1135
|
|
601
894
|
*
|
|
602
|
-
* @param {L.LatLngBounds} bounds
|
|
603
|
-
*
|
|
895
|
+
* @param {L.LatLngBounds} bounds Geographic bounds to project.
|
|
896
|
+
*
|
|
897
|
+
* @returns {L.Bounds} Axis-aligned bounds in container-pixel coordinates.
|
|
604
898
|
*
|
|
605
|
-
* @since leaflet-rotate (v0.2)
|
|
899
|
+
* @since leaflet-rotate (v0.2.0)
|
|
606
900
|
*/
|
|
607
|
-
mapBoundsToContainerBounds
|
|
901
|
+
mapBoundsToContainerBounds(bounds) {
|
|
608
902
|
if (!this._rotate && mapProto.mapBoundsToContainerBounds) {
|
|
609
903
|
return mapProto.mapBoundsToContainerBounds.apply(this, arguments);
|
|
610
904
|
}
|
|
611
905
|
const origin = this.getPixelOrigin();
|
|
612
906
|
const nw = this.layerPointToContainerPoint(this.project(bounds.getNorthWest())._subtract(origin)), ne = this.layerPointToContainerPoint(this.project(bounds.getNorthEast())._subtract(origin)), sw = this.layerPointToContainerPoint(this.project(bounds.getSouthWest())._subtract(origin)), se = this.layerPointToContainerPoint(this.project(bounds.getSouthEast())._subtract(origin));
|
|
613
|
-
return L.
|
|
614
|
-
L.
|
|
907
|
+
return new L.Bounds([
|
|
908
|
+
new L.Point(Math.min(nw.x, ne.x, se.x, sw.x), Math.min(nw.y, ne.y, se.y, sw.y)),
|
|
615
909
|
// [ minX, minY ]
|
|
616
|
-
L.
|
|
910
|
+
new L.Point(Math.max(nw.x, ne.x, se.x, sw.x), Math.max(nw.y, ne.y, se.y, sw.y))
|
|
617
911
|
// [ maxX, maxY ]
|
|
618
912
|
]);
|
|
619
913
|
},
|
|
620
914
|
/**
|
|
621
|
-
*
|
|
915
|
+
* Return the geographic bounds of the current viewport, accounting for rotation and pitch.
|
|
622
916
|
*
|
|
623
|
-
*
|
|
624
|
-
* function should match the `rotatePane` or `norotatePane` bounds
|
|
917
|
+
* The result is the smallest geographic bounds containing all four viewport corners.
|
|
625
918
|
*
|
|
626
|
-
* @
|
|
919
|
+
* @returns {L.LatLngBounds} Geographic bounds visible in the map viewport.
|
|
627
920
|
*
|
|
628
|
-
* @
|
|
921
|
+
* @since leaflet-rotate (v0.1.0)
|
|
629
922
|
*/
|
|
630
|
-
getBounds
|
|
923
|
+
getBounds() {
|
|
631
924
|
if (!this._rotate) {
|
|
632
925
|
return mapProto.getBounds.apply(this, arguments);
|
|
633
926
|
}
|
|
634
|
-
|
|
927
|
+
const size = this.getSize();
|
|
635
928
|
return new L.LatLngBounds([
|
|
636
929
|
this.containerPointToLatLng([0, 0]),
|
|
637
930
|
// topleft
|
|
@@ -660,70 +953,199 @@ var leafletRotate = (() => {
|
|
|
660
953
|
// return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
|
|
661
954
|
// },
|
|
662
955
|
/**
|
|
663
|
-
*
|
|
956
|
+
* Set the map bearing, optionally animating to the new value.
|
|
957
|
+
*
|
|
958
|
+
* @param {number} theta Target bearing in degrees; normalized to [0, 360).
|
|
959
|
+
* @param {Object} [options] Animation options.
|
|
960
|
+
* @param {boolean} [options.animate=false] Animate the change.
|
|
961
|
+
* @param {number} [options.duration=0.25] Animation duration in seconds.
|
|
962
|
+
* @param {number} [options.easeLinearity=0.5] Easing curve linearity.
|
|
664
963
|
*
|
|
665
|
-
* @
|
|
666
|
-
* @param {Object} [options] animation options
|
|
667
|
-
* @param {boolean} [options.animate=false]
|
|
668
|
-
* @param {number} [options.duration=0.25] animation duration in seconds
|
|
669
|
-
* @param {number} [options.easeLinearity=0.5]
|
|
964
|
+
* @returns {void}
|
|
670
965
|
*
|
|
671
|
-
* @
|
|
966
|
+
* @fires L.Map#rotate
|
|
967
|
+
*
|
|
968
|
+
* @since leaflet-rotate (v0.1.0)
|
|
672
969
|
*/
|
|
673
|
-
setBearing
|
|
674
|
-
if (!
|
|
970
|
+
setBearing(theta, options) {
|
|
971
|
+
if (!this._rotate) {
|
|
675
972
|
return;
|
|
676
973
|
}
|
|
677
974
|
if (options && options.animate) {
|
|
678
975
|
this._bearingAnimationId = (this._bearingAnimationId || 0) + 1;
|
|
679
|
-
|
|
976
|
+
const animationId = this._bearingAnimationId;
|
|
680
977
|
if (this._bearingAnimationFrame) {
|
|
681
|
-
|
|
978
|
+
cancelAnimationFrame(this._bearingAnimationFrame);
|
|
682
979
|
}
|
|
683
|
-
|
|
684
|
-
|
|
980
|
+
const startBearing = this.getBearing(), endBearing = L.Util.wrapNum(theta, [0, 360]), delta = (endBearing - startBearing + 540) % 360 - 180, duration = Math.max(0, options.duration == null ? 0.25 : options.duration) * 1e3, easeLinearity = Math.max(options.easeLinearity || 0.5, 0.2), startTime = performance.now();
|
|
981
|
+
const animate = function() {
|
|
685
982
|
if (!this._mapPane) {
|
|
686
983
|
delete this._bearingAnimationFrame;
|
|
687
984
|
return;
|
|
688
985
|
}
|
|
689
|
-
|
|
986
|
+
const progress = duration ? Math.min((performance.now() - startTime) / duration, 1) : 1, eased = 1 - Math.pow(1 - progress, 1 / easeLinearity);
|
|
690
987
|
this._setBearing(progress === 1 ? endBearing : startBearing + delta * eased);
|
|
691
988
|
if (this._bearingAnimationId !== animationId) {
|
|
692
989
|
return;
|
|
693
990
|
}
|
|
694
991
|
if (progress < 1) {
|
|
695
|
-
this._bearingAnimationFrame =
|
|
992
|
+
this._bearingAnimationFrame = requestAnimationFrame(animate.bind(this));
|
|
696
993
|
} else {
|
|
697
994
|
delete this._bearingAnimationFrame;
|
|
698
995
|
}
|
|
699
996
|
};
|
|
700
|
-
this._bearingAnimationFrame =
|
|
997
|
+
this._bearingAnimationFrame = requestAnimationFrame(animate.bind(this));
|
|
701
998
|
return;
|
|
702
999
|
}
|
|
703
1000
|
this._bearingAnimationId = (this._bearingAnimationId || 0) + 1;
|
|
704
1001
|
if (this._bearingAnimationFrame) {
|
|
705
|
-
|
|
1002
|
+
cancelAnimationFrame(this._bearingAnimationFrame);
|
|
706
1003
|
delete this._bearingAnimationFrame;
|
|
707
1004
|
}
|
|
708
1005
|
this._setBearing(theta);
|
|
709
1006
|
},
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
1007
|
+
/**
|
|
1008
|
+
* Apply a bearing immediately and update the rotated pane and map coordinates.
|
|
1009
|
+
*
|
|
1010
|
+
* @param {number} theta Bearing in degrees.
|
|
1011
|
+
* @param {L.LatLng} [mapCenter] Center to preserve while applying the change.
|
|
1012
|
+
*
|
|
1013
|
+
* @fires L.Map#rotate
|
|
1014
|
+
*
|
|
1015
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1016
|
+
*/
|
|
1017
|
+
_setBearing(theta, mapCenter) {
|
|
1018
|
+
if (!mapCenter && this._loaded) {
|
|
1019
|
+
mapCenter = this.getCenter();
|
|
1020
|
+
}
|
|
1021
|
+
const bearing = L.Util.wrapNum(theta, [0, 360]) * (Math.PI / 180), center = this._getPixelCenter(), oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center), newPos = oldPos.rotateFrom(bearing, center);
|
|
713
1022
|
this._pivot = center;
|
|
714
1023
|
this._bearing = bearing;
|
|
715
1024
|
this._rotatePanePos = newPos;
|
|
1025
|
+
if (this._pitch) {
|
|
1026
|
+
this._rotatePane._leaflet_pos = oldPos;
|
|
1027
|
+
this._updatePitchTransform();
|
|
1028
|
+
} else {
|
|
1029
|
+
L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center);
|
|
1030
|
+
}
|
|
1031
|
+
if (mapCenter) {
|
|
1032
|
+
this._pixelOrigin = this.project(mapCenter, this._zoom).subtract(this.containerPointToLayerPoint(this.getSize().divideBy(2)));
|
|
1033
|
+
delete this._lastCenter;
|
|
1034
|
+
this.fire("move");
|
|
1035
|
+
}
|
|
716
1036
|
this.fire("rotate");
|
|
717
1037
|
},
|
|
718
1038
|
/**
|
|
719
|
-
*
|
|
1039
|
+
* Transform a point with a projective matrix, accepting DOMMatrix or its 2D inverse.
|
|
1040
|
+
*
|
|
1041
|
+
* @param {L.Point|number[]} point Input pixel coordinate.
|
|
1042
|
+
* @param {DOMMatrix|Object} matrix Forward or inverse transformation matrix.
|
|
1043
|
+
*
|
|
1044
|
+
* @returns {L.Point} Transformed pixel coordinate.
|
|
1045
|
+
*
|
|
1046
|
+
* @since leaflet-rotate (0.5.0)
|
|
1047
|
+
*/
|
|
1048
|
+
_transformPitchPoint(point, matrix) {
|
|
1049
|
+
point = toPoint(point);
|
|
1050
|
+
if (!(matrix instanceof DOMMatrix)) {
|
|
1051
|
+
const denominator = matrix.g * point.x + matrix.h * point.y + matrix.i;
|
|
1052
|
+
return new L.Point(
|
|
1053
|
+
(matrix.a * point.x + matrix.b * point.y + matrix.c) / denominator,
|
|
1054
|
+
(matrix.d * point.x + matrix.e * point.y + matrix.f) / denominator
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
const transformed = new DOMPoint(point.x, point.y).matrixTransform(matrix);
|
|
1058
|
+
return new L.Point(transformed.x / transformed.w, transformed.y / transformed.w);
|
|
1059
|
+
},
|
|
1060
|
+
/**
|
|
1061
|
+
* Apply the current pitch/bearing CSS transform and cache its forward and inverse matrices.
|
|
1062
|
+
*
|
|
1063
|
+
* @since leaflet-rotate (0.5.0)
|
|
1064
|
+
*/
|
|
1065
|
+
_updatePitchTransform() {
|
|
1066
|
+
if (!this._pitch || !this._rotatePane) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
const pivot = this._getPixelCenter(), size = this.getSize(), perspective = Math.max(1, size.y * 1.5), position = this._rotatePane._leaflet_pos || new L.Point(0, 0), 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)`;
|
|
1070
|
+
this._rotatePane.style.transformOrigin = "0 0";
|
|
1071
|
+
this._rotatePane.style.transform = transform;
|
|
1072
|
+
const matrix = this._pitchTransform = new DOMMatrix(getComputedStyle(this._rotatePane).transform), a = matrix.m11, b = matrix.m21, c = matrix.m41, d = matrix.m12, e = matrix.m22, f = matrix.m42, g = matrix.m14, h = matrix.m24, i = matrix.m44, determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
|
|
1073
|
+
this._pitchInverseTransform = {
|
|
1074
|
+
a: (e * i - f * h) / determinant,
|
|
1075
|
+
b: (c * h - b * i) / determinant,
|
|
1076
|
+
c: (b * f - c * e) / determinant,
|
|
1077
|
+
d: (f * g - d * i) / determinant,
|
|
1078
|
+
e: (a * i - c * g) / determinant,
|
|
1079
|
+
f: (c * d - a * f) / determinant,
|
|
1080
|
+
g: (d * h - e * g) / determinant,
|
|
1081
|
+
h: (b * g - a * h) / determinant,
|
|
1082
|
+
i: (a * e - b * d) / determinant
|
|
1083
|
+
};
|
|
1084
|
+
},
|
|
1085
|
+
/**
|
|
1086
|
+
* Set the map pitch in degrees. Values are clamped to the range 0–85.
|
|
1087
|
+
*
|
|
1088
|
+
* @param {number} pitch Pitch angle in degrees; clamped to [0, 85].
|
|
1089
|
+
*
|
|
1090
|
+
* @returns {L.Map} This map instance.
|
|
1091
|
+
*
|
|
1092
|
+
* @fires L.Map#pitch
|
|
1093
|
+
*
|
|
1094
|
+
* @since leaflet-rotate (0.5.0)
|
|
1095
|
+
*/
|
|
1096
|
+
setPitch(pitch) {
|
|
1097
|
+
this._setPitchAndBearing(pitch, this.getBearing());
|
|
1098
|
+
return this;
|
|
1099
|
+
},
|
|
1100
|
+
/**
|
|
1101
|
+
* Update pitch and bearing together, preserving the current map center.
|
|
1102
|
+
* Used by the Ctrl+drag handler to avoid applying two separate transforms.
|
|
1103
|
+
*
|
|
1104
|
+
* @param {number} pitch Pitch angle in degrees.
|
|
1105
|
+
* @param {number} bearing Bearing angle in degrees.
|
|
1106
|
+
*
|
|
1107
|
+
* @fires L.Map#pitch
|
|
1108
|
+
* @fires L.Map#rotate
|
|
1109
|
+
*
|
|
1110
|
+
* @since leaflet-rotate (0.5.0)
|
|
1111
|
+
*/
|
|
1112
|
+
_setPitchAndBearing(pitch, bearing) {
|
|
1113
|
+
if (!this._rotate) {
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
const clampedPitch = Math.max(0, Math.min(pitch, 85)) * (Math.PI / 180), clampedBearing = L.Util.wrapNum(bearing, [0, 360]) * (Math.PI / 180), pitchChanged = clampedPitch !== this._pitch;
|
|
1117
|
+
if (!pitchChanged && clampedBearing === this._bearing) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
const mapCenter = this._loaded ? this.getCenter() : void 0;
|
|
1121
|
+
if (pitchChanged) {
|
|
1122
|
+
this._pitch = clampedPitch;
|
|
1123
|
+
this._rotatePane.style.transformOrigin = L.version.startsWith("2.") ? "0 0" : "";
|
|
1124
|
+
}
|
|
1125
|
+
this._setBearing(bearing, mapCenter);
|
|
1126
|
+
if (pitchChanged) {
|
|
1127
|
+
this.fire("pitch");
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
1130
|
+
/**
|
|
1131
|
+
* Get the current map pitch in degrees.
|
|
720
1132
|
*
|
|
721
|
-
* @returns {number}
|
|
1133
|
+
* @returns {number} Pitch angle in degrees.
|
|
722
1134
|
*
|
|
723
|
-
* @since leaflet-rotate (
|
|
1135
|
+
* @since leaflet-rotate (0.5.0)
|
|
724
1136
|
*/
|
|
725
|
-
|
|
726
|
-
return this.
|
|
1137
|
+
getPitch() {
|
|
1138
|
+
return (this._pitch || 0) * (180 / Math.PI);
|
|
1139
|
+
},
|
|
1140
|
+
/**
|
|
1141
|
+
* Get the current map bearing. Enable the `rotate` map option before calling.
|
|
1142
|
+
*
|
|
1143
|
+
* @returns {number} Bearing in degrees, normalized to [0, 360).
|
|
1144
|
+
*
|
|
1145
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1146
|
+
*/
|
|
1147
|
+
getBearing() {
|
|
1148
|
+
return this._bearing * (180 / Math.PI);
|
|
727
1149
|
},
|
|
728
1150
|
/**
|
|
729
1151
|
* Creates a new [map pane](#map-pane) with the given name if it doesn't
|
|
@@ -732,9 +1154,12 @@ var leafletRotate = (() => {
|
|
|
732
1154
|
*
|
|
733
1155
|
* @param {String} name leaflet pane
|
|
734
1156
|
* @param {HTMLElement} [container] parent element
|
|
1157
|
+
*
|
|
735
1158
|
* @returns {HTMLElement} pane container
|
|
1159
|
+
*
|
|
1160
|
+
* @since leaflet-rotate (v0.1.0)
|
|
736
1161
|
*/
|
|
737
|
-
// createPane
|
|
1162
|
+
// createPane(name, container) {
|
|
738
1163
|
// if (!this._rotate || name == 'mapPane') {
|
|
739
1164
|
// return mapProto.createPane.apply(this, arguments);
|
|
740
1165
|
// }
|
|
@@ -762,14 +1187,23 @@ var leafletRotate = (() => {
|
|
|
762
1187
|
* - tooltipPane [HTMLElement = 650] - Pane for tooltips.
|
|
763
1188
|
* - popupPane [HTMLElement = 700] - Pane for popups.
|
|
764
1189
|
*/
|
|
765
|
-
|
|
766
|
-
|
|
1190
|
+
/**
|
|
1191
|
+
* Create the map's panes, placing tile/vector layers in the transformed pane
|
|
1192
|
+
* and markers/overlays in the pane that remains upright.
|
|
1193
|
+
*
|
|
1194
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1195
|
+
*/
|
|
1196
|
+
_initPanes() {
|
|
1197
|
+
const panes = this._panes = {};
|
|
767
1198
|
this._paneRenderers = {};
|
|
768
1199
|
this._mapPane = this.createPane("mapPane", this._container);
|
|
769
1200
|
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
|
|
770
1201
|
if (this._rotate) {
|
|
771
1202
|
this._rotatePane = this.createPane("rotatePane", this._mapPane);
|
|
772
1203
|
this._norotatePane = this.createPane("norotatePane", this._mapPane);
|
|
1204
|
+
if (L.version.startsWith("2.")) {
|
|
1205
|
+
this._rotatePane.style.transformOrigin = "0 0";
|
|
1206
|
+
}
|
|
773
1207
|
this.createPane("tilePane", this._rotatePane);
|
|
774
1208
|
this.createPane("overlayPane", this._rotatePane);
|
|
775
1209
|
this.createPane("shadowPane", this._norotatePane);
|
|
@@ -785,8 +1219,8 @@ var leafletRotate = (() => {
|
|
|
785
1219
|
this.createPane("popupPane");
|
|
786
1220
|
}
|
|
787
1221
|
if (!this.options.markerZoomAnimation) {
|
|
788
|
-
|
|
789
|
-
|
|
1222
|
+
panes.markerPane.classList.add("leaflet-zoom-hide");
|
|
1223
|
+
panes.shadowPane.classList.add("leaflet-zoom-hide");
|
|
790
1224
|
}
|
|
791
1225
|
},
|
|
792
1226
|
/**
|
|
@@ -797,17 +1231,19 @@ var leafletRotate = (() => {
|
|
|
797
1231
|
*
|
|
798
1232
|
* @see https://github.com/Raruto/leaflet-rotate/issues/18
|
|
799
1233
|
*
|
|
800
|
-
* @param {L.LatLng} latlng
|
|
801
|
-
* @param {Object} [options={}] padding
|
|
1234
|
+
* @param {L.LatLng} latlng Geographic point to keep visible.
|
|
1235
|
+
* @param {Object} [options={}] Padding options (`padding`, `paddingTopLeft`, `paddingBottomRight`).
|
|
802
1236
|
*
|
|
803
1237
|
* @returns {L.Map} current map instance
|
|
1238
|
+
*
|
|
1239
|
+
* @since leaflet-rotate (v0.1.4)
|
|
804
1240
|
*/
|
|
805
1241
|
panInside(latlng, options) {
|
|
806
|
-
if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
|
|
1242
|
+
if (!this._rotate || !this._pitch && Math.abs(this._bearing).toFixed(1) < 0.1) {
|
|
807
1243
|
return mapProto.panInside.apply(this, arguments);
|
|
808
1244
|
}
|
|
809
1245
|
options = options || {};
|
|
810
|
-
const paddingTL =
|
|
1246
|
+
const paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), pixelPoint = this.latLngToContainerPoint(latlng), pixelBounds = new L.Bounds([new L.Point(0, 0), this.getSize()]), pixelCenter = pixelBounds.getCenter(), paddedBounds = new L.Bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]), paddedSize = paddedBounds.getSize();
|
|
811
1247
|
if (!paddedBounds.contains(pixelPoint)) {
|
|
812
1248
|
this._enforcingBounds = true;
|
|
813
1249
|
const centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
|
|
@@ -829,13 +1265,16 @@ var leafletRotate = (() => {
|
|
|
829
1265
|
*
|
|
830
1266
|
* @see https://github.com/fnicollet/Leaflet/issues/7
|
|
831
1267
|
*
|
|
832
|
-
* @param {L.LatLngBounds} bounds
|
|
833
|
-
* @param {Object} [options]
|
|
834
|
-
*
|
|
1268
|
+
* @param {L.LatLngBounds} bounds Geographic bounds to fit inside the viewport.
|
|
1269
|
+
* @param {Object} [options] Pan animation options.
|
|
1270
|
+
*
|
|
1271
|
+
* @returns {L.Map} This map instance.
|
|
1272
|
+
*
|
|
1273
|
+
* @since leaflet-rotate (v0.1.0)
|
|
835
1274
|
*/
|
|
836
|
-
// panInsideBounds
|
|
1275
|
+
// panInsideBounds(bounds, options) {
|
|
837
1276
|
// this._enforcingBounds = true;
|
|
838
|
-
//
|
|
1277
|
+
// const center = this.getCenter(),
|
|
839
1278
|
// newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
|
|
840
1279
|
//
|
|
841
1280
|
// if (!center.equals(newCenter)) {
|
|
@@ -896,18 +1335,20 @@ var leafletRotate = (() => {
|
|
|
896
1335
|
* instead returns the minimum zoom level on which the map view fits into
|
|
897
1336
|
* the given bounds in its entirety.
|
|
898
1337
|
*
|
|
899
|
-
* @param {L.LatLngBounds} bounds
|
|
900
|
-
* @param {
|
|
901
|
-
* @param {L.Point} [padding=[0,0]]
|
|
1338
|
+
* @param {L.LatLngBounds} bounds Geographic bounds to fit.
|
|
1339
|
+
* @param {boolean} [inside=false] Fit the viewport inside bounds instead of bounds inside the viewport.
|
|
1340
|
+
* @param {L.Point|number[]} [padding=[0,0]] Pixel padding around the viewport.
|
|
1341
|
+
*
|
|
1342
|
+
* @returns {number} Zoom level satisfying the fit.
|
|
902
1343
|
*
|
|
903
|
-
* @
|
|
1344
|
+
* @since leaflet-rotate (v0.1.0)
|
|
904
1345
|
*/
|
|
905
1346
|
getBoundsZoom(bounds, inside, padding) {
|
|
906
|
-
if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
|
|
1347
|
+
if (!this._rotate || !this._pitch && Math.abs(this._bearing).toFixed(1) < 0.1) {
|
|
907
1348
|
return mapProto.getBoundsZoom.apply(this, arguments);
|
|
908
1349
|
}
|
|
909
|
-
bounds = L.
|
|
910
|
-
padding =
|
|
1350
|
+
bounds = bounds instanceof L.LatLngBounds ? bounds : new L.LatLngBounds(bounds);
|
|
1351
|
+
padding = toPoint(padding || [0, 0]);
|
|
911
1352
|
let zoom = this.getZoom() || 0;
|
|
912
1353
|
const min = this.getMinZoom(), max = this.getMaxZoom(), size = this.getSize().subtract(padding), boundsSize = this.mapBoundsToContainerBounds(bounds).getSize(), snap = this.options.zoomSnap, scalex = size.x / boundsSize.x, scaley = size.y / boundsSize.y, scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
|
|
913
1354
|
zoom = this.getScaleZoom(scale, zoom);
|
|
@@ -922,50 +1363,78 @@ var leafletRotate = (() => {
|
|
|
922
1363
|
*
|
|
923
1364
|
* @returns {L.Point} layer center
|
|
924
1365
|
*/
|
|
925
|
-
// _getCenterLayerPoint
|
|
1366
|
+
// _getCenterLayerPoint() {
|
|
926
1367
|
// return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
|
|
927
1368
|
// },
|
|
928
1369
|
/**
|
|
929
|
-
*
|
|
1370
|
+
* Return the pixel offset from the current center to the given geographic point.
|
|
930
1371
|
*
|
|
931
|
-
* @param {L.LatLng} latlng
|
|
1372
|
+
* @param {L.LatLng} latlng Geographic point.
|
|
1373
|
+
*
|
|
1374
|
+
* @returns {L.Point} Offset in layer pixels, adjusted for bearing and pitch.
|
|
1375
|
+
*
|
|
1376
|
+
* @since leaflet-rotate (v0.1.0)
|
|
932
1377
|
*/
|
|
933
|
-
_getCenterOffset
|
|
934
|
-
|
|
935
|
-
if (this.
|
|
1378
|
+
_getCenterOffset(latlng) {
|
|
1379
|
+
let centerOffset = mapProto._getCenterOffset.apply(this, arguments);
|
|
1380
|
+
if (this._pitch) {
|
|
1381
|
+
const center = this.getSize()._divideBy(2);
|
|
1382
|
+
centerOffset = this.containerPointToLayerPoint(center.add(centerOffset)).subtract(this.containerPointToLayerPoint(center));
|
|
1383
|
+
} else if (this._rotate) {
|
|
936
1384
|
centerOffset = centerOffset.rotate(this._bearing);
|
|
937
1385
|
}
|
|
938
1386
|
return centerOffset;
|
|
939
1387
|
},
|
|
940
1388
|
/**
|
|
941
|
-
*
|
|
1389
|
+
* Return the current position of the rotated pane.
|
|
1390
|
+
*
|
|
1391
|
+
* @returns {L.Point} Rotated-pane position in map-pane coordinates.
|
|
1392
|
+
*
|
|
1393
|
+
* @since leaflet-rotate (v0.1.0)
|
|
942
1394
|
*/
|
|
943
|
-
_getRotatePanePos
|
|
1395
|
+
_getRotatePanePos() {
|
|
944
1396
|
return this._rotatePanePos || new L.Point(0, 0);
|
|
945
1397
|
},
|
|
946
1398
|
// _latLngToNewLayerPoint(latlng, zoom, center) {
|
|
947
1399
|
// const topLeft = this._getNewPixelOrigin(center, zoom);
|
|
948
1400
|
// return this.project(latlng, zoom)._subtract(topLeft);
|
|
949
1401
|
//},
|
|
950
|
-
|
|
951
|
-
|
|
1402
|
+
/**
|
|
1403
|
+
* Calculate the pixel origin for a target view while preserving bearing-aware alignment.
|
|
1404
|
+
*
|
|
1405
|
+
* @param {L.LatLng} center Target center.
|
|
1406
|
+
* @param {number} zoom Target zoom level.
|
|
1407
|
+
*
|
|
1408
|
+
* @returns {L.Point} New pixel origin.
|
|
1409
|
+
*
|
|
1410
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1411
|
+
*/
|
|
1412
|
+
_getNewPixelOrigin(center, zoom) {
|
|
1413
|
+
if (!this._rotate || L.version.startsWith("2.")) {
|
|
952
1414
|
return mapProto._getNewPixelOrigin.apply(this, arguments);
|
|
953
1415
|
}
|
|
954
|
-
|
|
1416
|
+
const viewHalf = this.getSize()._divideBy(2);
|
|
955
1417
|
return this.project(center, zoom).rotate(this._bearing)._subtract(viewHalf)._add(this._getMapPanePos())._add(this._getRotatePanePos()).rotate(-this._bearing)._round();
|
|
956
1418
|
},
|
|
957
1419
|
/**
|
|
958
|
-
*
|
|
1420
|
+
* Calculate the pixel bounds needed to cover the transformed viewport.
|
|
1421
|
+
*
|
|
1422
|
+
* @param {L.LatLng} [center=this.getCenter()] Target center.
|
|
1423
|
+
* @param {number} [zoom=this.getZoom()] Target zoom level.
|
|
1424
|
+
*
|
|
1425
|
+
* @returns {L.Bounds} Pixel bounds in projected map coordinates.
|
|
1426
|
+
*
|
|
1427
|
+
* @since leaflet-rotate (v0.2.0)
|
|
959
1428
|
*
|
|
960
1429
|
* @see src\layer\tile\GridLayer::_getTiledPixelBounds()
|
|
961
1430
|
*/
|
|
962
|
-
_getNewPixelBounds
|
|
1431
|
+
_getNewPixelBounds(center, zoom) {
|
|
963
1432
|
center = center || this.getCenter();
|
|
964
1433
|
zoom = zoom || this.getZoom();
|
|
965
1434
|
if (!this._rotate && mapProto._getNewPixelBounds) {
|
|
966
1435
|
return mapProto._getNewPixelBounds.apply(this, arguments);
|
|
967
1436
|
}
|
|
968
|
-
|
|
1437
|
+
const mapZoom = this._animatingZoom ? Math.max(this._animateToZoom, this.getZoom()) : this.getZoom(), scale = this.getZoomScale(mapZoom, zoom), pixelCenter = this.project(center, zoom).floor(), size = this.getSize(), halfSize = new L.Bounds([
|
|
969
1438
|
this.containerPointToLayerPoint([0, 0]).floor(),
|
|
970
1439
|
this.containerPointToLayerPoint([size.x, 0]).floor(),
|
|
971
1440
|
this.containerPointToLayerPoint([0, size.y]).floor(),
|
|
@@ -974,26 +1443,30 @@ var leafletRotate = (() => {
|
|
|
974
1443
|
return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
|
|
975
1444
|
},
|
|
976
1445
|
/**
|
|
977
|
-
* @since leaflet-rotate (v0.2)
|
|
1446
|
+
* @since leaflet-rotate (v0.2.0)
|
|
978
1447
|
*
|
|
979
|
-
* @
|
|
1448
|
+
* @returns {L.Point} Map pivot point in pixel coordinates.
|
|
980
1449
|
*/
|
|
981
|
-
_getPixelCenter
|
|
1450
|
+
_getPixelCenter() {
|
|
982
1451
|
if (!this._rotate && mapProto._getPixelCenter) {
|
|
983
1452
|
return mapProto._getPixelCenter.apply(this, arguments);
|
|
984
1453
|
}
|
|
985
1454
|
return this.getSize()._divideBy(2)._subtract(this._getMapPanePos());
|
|
986
1455
|
},
|
|
987
1456
|
/**
|
|
988
|
-
* @since leaflet-rotate (v0.2)
|
|
989
|
-
*
|
|
990
1457
|
* @see src\layer\vector\Renderer::_update()
|
|
1458
|
+
*
|
|
1459
|
+
* @param {number} padding Bounds padding as a fraction of map size.
|
|
1460
|
+
*
|
|
1461
|
+
* @returns {L.Bounds} Padded bounds in layer-point coordinates.
|
|
1462
|
+
*
|
|
1463
|
+
* @since leaflet-rotate (v0.2.0)
|
|
991
1464
|
*/
|
|
992
|
-
_getPaddedPixelBounds
|
|
1465
|
+
_getPaddedPixelBounds(padding) {
|
|
993
1466
|
if (!this._rotate && mapProto._getPaddedPixelBounds) {
|
|
994
1467
|
return mapProto._getPaddedPixelBounds.apply(this, arguments);
|
|
995
1468
|
}
|
|
996
|
-
|
|
1469
|
+
const p = padding, size = this.getSize(), padMin = size.multiplyBy(-p), padMax = size.multiplyBy(1 + p);
|
|
997
1470
|
return new L.Bounds([
|
|
998
1471
|
this.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),
|
|
999
1472
|
this.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),
|
|
@@ -1001,23 +1474,32 @@ var leafletRotate = (() => {
|
|
|
1001
1474
|
this.containerPointToLayerPoint([padMax.x, padMax.y]).floor()
|
|
1002
1475
|
]);
|
|
1003
1476
|
},
|
|
1004
|
-
|
|
1477
|
+
/**
|
|
1478
|
+
* Build and fire Leaflet's location-found event, including the device heading.
|
|
1479
|
+
*
|
|
1480
|
+
* @param {GeolocationPosition} pos Browser geolocation result.
|
|
1481
|
+
*
|
|
1482
|
+
* @fires L.Map#locationfound
|
|
1483
|
+
*
|
|
1484
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1485
|
+
*/
|
|
1486
|
+
_handleGeolocationResponse(pos) {
|
|
1005
1487
|
if (!this._container._leaflet_id) {
|
|
1006
1488
|
return;
|
|
1007
1489
|
}
|
|
1008
|
-
|
|
1490
|
+
const lat = pos.coords.latitude, lng = pos.coords.longitude, hdg = pos.coords.heading, latlng = new L.LatLng(lat, lng), bounds = latlng.toBounds(pos.coords.accuracy), options = this._locateOptions;
|
|
1009
1491
|
if (options.setView) {
|
|
1010
|
-
|
|
1492
|
+
const zoom = this.getBoundsZoom(bounds);
|
|
1011
1493
|
this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
|
|
1012
1494
|
}
|
|
1013
|
-
|
|
1495
|
+
const data = {
|
|
1014
1496
|
latlng,
|
|
1015
1497
|
bounds,
|
|
1016
1498
|
timestamp: pos.timestamp,
|
|
1017
1499
|
/** @TODO use mapProto._handleGeolocationResponse */
|
|
1018
1500
|
heading: hdg
|
|
1019
1501
|
};
|
|
1020
|
-
for (
|
|
1502
|
+
for (const i in pos.coords) {
|
|
1021
1503
|
if (typeof pos.coords[i] === "number") {
|
|
1022
1504
|
data[i] = pos.coords[i];
|
|
1023
1505
|
}
|
|
@@ -1028,10 +1510,11 @@ var leafletRotate = (() => {
|
|
|
1028
1510
|
* @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/geo/LatLngBounds.js#L253-L264
|
|
1029
1511
|
*
|
|
1030
1512
|
* @param {L.Bounds} points
|
|
1513
|
+
*
|
|
1031
1514
|
* @returns {L.Bounds}
|
|
1032
1515
|
*/
|
|
1033
1516
|
// toCircumscribedBounds(points) {
|
|
1034
|
-
//
|
|
1517
|
+
// const minX = points.reduce(function (pv, v) { return Math.min(pv, v.x); }, points[0].x),
|
|
1035
1518
|
// maxX = points.reduce(function (pv, v) { return Math.max(pv, v.x); }, points[0].x),
|
|
1036
1519
|
// minY = points.reduce(function (pv, v) { return Math.min(pv, v.y); }, points[0].y),
|
|
1037
1520
|
// maxY = points.reduce(function (pv, v) { return Math.max(pv, v.y); }, points[0].y);
|
|
@@ -1042,7 +1525,12 @@ var leafletRotate = (() => {
|
|
|
1042
1525
|
|
|
1043
1526
|
// src/map/handler/CompassBearing.js
|
|
1044
1527
|
L.Map.CompassBearing = L.Handler.extend({
|
|
1045
|
-
|
|
1528
|
+
/**
|
|
1529
|
+
* @param {L.Map} map Map whose bearing follows device orientation.
|
|
1530
|
+
*
|
|
1531
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1532
|
+
*/
|
|
1533
|
+
initialize(map) {
|
|
1046
1534
|
this._map = map;
|
|
1047
1535
|
if ("ondeviceorientationabsolute" in window) {
|
|
1048
1536
|
this.__deviceOrientationEvent = "deviceorientationabsolute";
|
|
@@ -1051,19 +1539,31 @@ var leafletRotate = (() => {
|
|
|
1051
1539
|
}
|
|
1052
1540
|
this._throttled = L.Util.throttle(this._onDeviceOrientation, 100, this);
|
|
1053
1541
|
},
|
|
1054
|
-
|
|
1542
|
+
/**
|
|
1543
|
+
* Subscribe to the supported device-orientation event, if available.
|
|
1544
|
+
*
|
|
1545
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1546
|
+
*/
|
|
1547
|
+
addHooks() {
|
|
1055
1548
|
if (this._map._rotate && this.__deviceOrientationEvent) {
|
|
1056
1549
|
L.DomEvent.on(window, this.__deviceOrientationEvent, this._throttled, this);
|
|
1057
1550
|
} else {
|
|
1058
1551
|
this.disable();
|
|
1059
1552
|
}
|
|
1060
1553
|
},
|
|
1061
|
-
|
|
1554
|
+
/**
|
|
1555
|
+
* Unsubscribe from device orientation when the handler is disabled.
|
|
1556
|
+
*
|
|
1557
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1558
|
+
*/
|
|
1559
|
+
removeHooks() {
|
|
1062
1560
|
if (this._map._rotate && this.__deviceOrientationEvent) {
|
|
1063
1561
|
L.DomEvent.off(window, this.__deviceOrientationEvent, this._throttled, this);
|
|
1064
1562
|
}
|
|
1065
1563
|
},
|
|
1066
1564
|
/**
|
|
1565
|
+
* Apply a device orientation reading to the map bearing.
|
|
1566
|
+
*
|
|
1067
1567
|
* `DeviceOrientationEvent.absolute` - Indicates whether the device is providing absolute
|
|
1068
1568
|
* orientation values (relatives to Magnetic North) or
|
|
1069
1569
|
* using some arbitrary frame determined by the device.
|
|
@@ -1080,10 +1580,14 @@ var leafletRotate = (() => {
|
|
|
1080
1580
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/absolute
|
|
1081
1581
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/alpha
|
|
1082
1582
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation
|
|
1583
|
+
*
|
|
1584
|
+
* @param {DeviceOrientationEvent} e Orientation reading from the browser.
|
|
1585
|
+
*
|
|
1586
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1083
1587
|
*/
|
|
1084
|
-
_onDeviceOrientation
|
|
1085
|
-
|
|
1086
|
-
|
|
1588
|
+
_onDeviceOrientation(e) {
|
|
1589
|
+
let angle = e.webkitCompassHeading != null ? e.webkitCompassHeading : e.alpha;
|
|
1590
|
+
let deviceOrientation = 0;
|
|
1087
1591
|
if (!Number.isFinite(angle)) {
|
|
1088
1592
|
return;
|
|
1089
1593
|
}
|
|
@@ -1108,12 +1612,19 @@ var leafletRotate = (() => {
|
|
|
1108
1612
|
*
|
|
1109
1613
|
* @type {Boolean}
|
|
1110
1614
|
*
|
|
1615
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1616
|
+
*
|
|
1111
1617
|
* @see https://developer.mozilla.org/docs/Web/API/MutationObserver
|
|
1112
1618
|
*/
|
|
1113
1619
|
trackContainerMutation: false
|
|
1114
1620
|
});
|
|
1115
1621
|
L.Map.ContainerMutation = L.Handler.extend({
|
|
1116
|
-
|
|
1622
|
+
/**
|
|
1623
|
+
* Start observing the map container's style attribute.
|
|
1624
|
+
*
|
|
1625
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1626
|
+
*/
|
|
1627
|
+
addHooks() {
|
|
1117
1628
|
if (!this._observer) {
|
|
1118
1629
|
this._observer = new MutationObserver(L.Util.bind(this._map.invalidateSize, this._map));
|
|
1119
1630
|
}
|
|
@@ -1125,41 +1636,77 @@ var leafletRotate = (() => {
|
|
|
1125
1636
|
attributeFilter: ["style"]
|
|
1126
1637
|
});
|
|
1127
1638
|
},
|
|
1128
|
-
|
|
1639
|
+
/**
|
|
1640
|
+
* Stop observing container mutations.
|
|
1641
|
+
*
|
|
1642
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1643
|
+
*/
|
|
1644
|
+
removeHooks() {
|
|
1129
1645
|
this._observer.disconnect();
|
|
1130
1646
|
}
|
|
1131
1647
|
});
|
|
1132
1648
|
L.Map.addInitHook("addHandler", "trackContainerMutation", L.Map.ContainerMutation);
|
|
1133
1649
|
|
|
1134
1650
|
// src/map/handler/TouchGestures.js
|
|
1651
|
+
function touchToContainerPoint(map, touch) {
|
|
1652
|
+
const container = map.getContainer(), rect = container.getBoundingClientRect();
|
|
1653
|
+
return new L.Point(
|
|
1654
|
+
touch.clientX - rect.left - container.clientLeft,
|
|
1655
|
+
touch.clientY - rect.top - container.clientTop
|
|
1656
|
+
);
|
|
1657
|
+
}
|
|
1135
1658
|
L.Map.mergeOptions({
|
|
1136
1659
|
/**
|
|
1137
|
-
*
|
|
1138
|
-
* zoom beyond min/max zoom and then bounce back
|
|
1139
|
-
* when pinch-zooming.
|
|
1660
|
+
* Whether pinch zoom may temporarily exceed min/max zoom and bounce back.
|
|
1140
1661
|
*
|
|
1141
1662
|
* @type {Boolean}
|
|
1663
|
+
*
|
|
1664
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1142
1665
|
*/
|
|
1143
1666
|
bounceAtZoomLimits: true
|
|
1144
1667
|
});
|
|
1145
1668
|
L.Map.TouchGestures = L.Handler.extend({
|
|
1146
|
-
|
|
1669
|
+
/**
|
|
1670
|
+
* Initialize the shared handler from the map's touch zoom/rotation options.
|
|
1671
|
+
*
|
|
1672
|
+
* @param {L.Map} map Map whose touch gestures are handled.
|
|
1673
|
+
*
|
|
1674
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1675
|
+
*/
|
|
1676
|
+
initialize(map) {
|
|
1147
1677
|
this._map = map;
|
|
1148
1678
|
this.rotate = !!this._map.options.touchRotate;
|
|
1149
1679
|
this.zoom = !!this._map.options.touchZoom;
|
|
1150
1680
|
},
|
|
1151
|
-
|
|
1681
|
+
/**
|
|
1682
|
+
* Listen for the start of two-finger gestures on the map container.
|
|
1683
|
+
*
|
|
1684
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1685
|
+
*/
|
|
1686
|
+
addHooks() {
|
|
1152
1687
|
L.DomEvent.on(this._map._container, "touchstart", this._onTouchStart, this);
|
|
1153
1688
|
},
|
|
1154
|
-
|
|
1689
|
+
/**
|
|
1690
|
+
* Remove the touch-start listener.
|
|
1691
|
+
*
|
|
1692
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1693
|
+
*/
|
|
1694
|
+
removeHooks() {
|
|
1155
1695
|
L.DomEvent.off(this._map._container, "touchstart", this._onTouchStart, this);
|
|
1156
1696
|
},
|
|
1157
|
-
|
|
1158
|
-
|
|
1697
|
+
/**
|
|
1698
|
+
* Record initial touch points and start tracking a two-finger gesture.
|
|
1699
|
+
*
|
|
1700
|
+
* @param {TouchEvent} e Touch-start event.
|
|
1701
|
+
*
|
|
1702
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1703
|
+
*/
|
|
1704
|
+
_onTouchStart(e) {
|
|
1705
|
+
const map = this._map;
|
|
1159
1706
|
if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming || this._rotating) {
|
|
1160
1707
|
return;
|
|
1161
1708
|
}
|
|
1162
|
-
|
|
1709
|
+
const p1 = touchToContainerPoint(map, e.touches[0]), p2 = touchToContainerPoint(map, e.touches[1]), vector = p1.subtract(p2);
|
|
1163
1710
|
this._centerPoint = map.getSize()._divideBy(2);
|
|
1164
1711
|
this._startLatLng = map.containerPointToLatLng(this._centerPoint);
|
|
1165
1712
|
if (this.zoom) {
|
|
@@ -1187,14 +1734,22 @@ var leafletRotate = (() => {
|
|
|
1187
1734
|
L.DomEvent.on(document, "touchmove", this._onTouchMove, this).on(document, "touchend touchcancel", this._onTouchEnd, this);
|
|
1188
1735
|
L.DomEvent.preventDefault(e);
|
|
1189
1736
|
},
|
|
1190
|
-
|
|
1737
|
+
/**
|
|
1738
|
+
* Update bearing and/or zoom while the two touch points move.
|
|
1739
|
+
*
|
|
1740
|
+
* @param {TouchEvent} e Touch-move event.
|
|
1741
|
+
*
|
|
1742
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1743
|
+
*/
|
|
1744
|
+
_onTouchMove(e) {
|
|
1191
1745
|
if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) {
|
|
1192
1746
|
return;
|
|
1193
1747
|
}
|
|
1194
|
-
|
|
1748
|
+
const map = this._map, p1 = touchToContainerPoint(map, e.touches[0]), p2 = touchToContainerPoint(map, e.touches[1]), vector = p1.subtract(p2), scale = p1.distanceTo(p2) / this._startDist;
|
|
1749
|
+
let delta;
|
|
1195
1750
|
if (this._rotating) {
|
|
1196
|
-
|
|
1197
|
-
|
|
1751
|
+
const theta = Math.atan(vector.x / vector.y);
|
|
1752
|
+
let bearingDelta = (theta - this._startTheta) * (180 / Math.PI);
|
|
1198
1753
|
if (vector.y < 0) {
|
|
1199
1754
|
bearingDelta += 180;
|
|
1200
1755
|
}
|
|
@@ -1217,17 +1772,16 @@ var leafletRotate = (() => {
|
|
|
1217
1772
|
if (scale === 1 && delta.x === 0 && delta.y === 0) {
|
|
1218
1773
|
return;
|
|
1219
1774
|
}
|
|
1220
|
-
|
|
1221
|
-
this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(
|
|
1775
|
+
const mapDelta = map._pitch ? map.containerPointToLayerPoint(this._centerPoint.add(delta)).subtract(map.containerPointToLayerPoint(this._centerPoint)) : delta.rotate(-map.getBearing() * (Math.PI / 180));
|
|
1776
|
+
this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(mapDelta));
|
|
1222
1777
|
}
|
|
1223
1778
|
}
|
|
1224
1779
|
if (!this._moved) {
|
|
1225
1780
|
map._moveStart(true, false);
|
|
1226
1781
|
this._moved = true;
|
|
1227
1782
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
|
|
1783
|
+
const moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, void 0);
|
|
1784
|
+
moveFn();
|
|
1231
1785
|
if (this.zoom) {
|
|
1232
1786
|
if (map.options.zoomAnimation) {
|
|
1233
1787
|
map._animateZoomNoDelay(this._center, map._limitZoom(this._zoom));
|
|
@@ -1237,14 +1791,18 @@ var leafletRotate = (() => {
|
|
|
1237
1791
|
}
|
|
1238
1792
|
L.DomEvent.preventDefault(e);
|
|
1239
1793
|
},
|
|
1240
|
-
|
|
1794
|
+
/**
|
|
1795
|
+
* Finish the gesture, settle the zoom and remove document listeners.
|
|
1796
|
+
*
|
|
1797
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1798
|
+
*/
|
|
1799
|
+
_onTouchEnd() {
|
|
1241
1800
|
if (!this._moved || !(this._zooming || this._rotating)) {
|
|
1242
1801
|
this._zooming = false;
|
|
1243
1802
|
return;
|
|
1244
1803
|
}
|
|
1245
1804
|
this._zooming = false;
|
|
1246
1805
|
this._rotating = false;
|
|
1247
|
-
L.Util.cancelAnimFrame(this._animRequest);
|
|
1248
1806
|
L.DomEvent.off(document, "touchmove", this._onTouchMove, this).off(document, "touchend touchcancel", this._onTouchEnd, this);
|
|
1249
1807
|
if (this.zoom) {
|
|
1250
1808
|
if (this._map.options.zoomAnimation) {
|
|
@@ -1263,15 +1821,27 @@ var leafletRotate = (() => {
|
|
|
1263
1821
|
* Whether the map can be rotated with a two-finger rotation gesture
|
|
1264
1822
|
*
|
|
1265
1823
|
* @type {Boolean}
|
|
1824
|
+
*
|
|
1825
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1266
1826
|
*/
|
|
1267
1827
|
touchRotate: false
|
|
1268
1828
|
});
|
|
1269
1829
|
L.Map.TouchRotate = L.Handler.extend({
|
|
1270
|
-
|
|
1830
|
+
/**
|
|
1831
|
+
* Enable rotation handling in the map's shared touch gesture handler.
|
|
1832
|
+
*
|
|
1833
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1834
|
+
*/
|
|
1835
|
+
addHooks() {
|
|
1271
1836
|
this._map.touchGestures.enable();
|
|
1272
1837
|
this._map.touchGestures.rotate = true;
|
|
1273
1838
|
},
|
|
1274
|
-
|
|
1839
|
+
/**
|
|
1840
|
+
* Disable rotation handling without affecting pinch zoom.
|
|
1841
|
+
*
|
|
1842
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1843
|
+
*/
|
|
1844
|
+
removeHooks() {
|
|
1275
1845
|
this._map.touchGestures.rotate = false;
|
|
1276
1846
|
}
|
|
1277
1847
|
});
|
|
@@ -1280,33 +1850,170 @@ var leafletRotate = (() => {
|
|
|
1280
1850
|
// src/map/handler/ShiftKeyRotate.js
|
|
1281
1851
|
L.Map.mergeOptions({
|
|
1282
1852
|
/**
|
|
1283
|
-
* Whether the map
|
|
1853
|
+
* Whether Shift+wheel changes the map bearing.
|
|
1854
|
+
*
|
|
1855
|
+
* @type {Boolean}
|
|
1856
|
+
*
|
|
1857
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1858
|
+
*/
|
|
1859
|
+
shiftKeyRotate: true,
|
|
1860
|
+
/**
|
|
1861
|
+
* Whether Ctrl+drag changes bearing and pitch.
|
|
1862
|
+
*
|
|
1284
1863
|
* @type {Boolean}
|
|
1864
|
+
*
|
|
1865
|
+
* @since leaflet-rotate (0.5.0)
|
|
1285
1866
|
*/
|
|
1286
|
-
|
|
1867
|
+
pitchDrag: true
|
|
1287
1868
|
});
|
|
1288
1869
|
L.Map.ShiftKeyRotate = L.Handler.extend({
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1870
|
+
/**
|
|
1871
|
+
* @param {L.Map} map Map controlled by this handler.
|
|
1872
|
+
*
|
|
1873
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1874
|
+
*/
|
|
1875
|
+
initialize(map) {
|
|
1876
|
+
this._map = map;
|
|
1877
|
+
this._onMouseDownBound = this._onMouseDown.bind(this);
|
|
1878
|
+
this._onMouseMoveBound = this._onMouseMove.bind(this);
|
|
1879
|
+
this._onMouseUpBound = this._onMouseUp.bind(this);
|
|
1292
1880
|
},
|
|
1293
|
-
|
|
1881
|
+
/**
|
|
1882
|
+
* Attach enabled wheel and pointer/mouse gesture listeners.
|
|
1883
|
+
*
|
|
1884
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1885
|
+
*/
|
|
1886
|
+
addHooks() {
|
|
1887
|
+
if (this._map.options.shiftKeyRotate) {
|
|
1888
|
+
L.DomEvent.on(this._map._container, "wheel", this._handleShiftScroll, this);
|
|
1889
|
+
}
|
|
1890
|
+
if (this._map.options.pitchDrag) {
|
|
1891
|
+
this.addPitchHooks();
|
|
1892
|
+
}
|
|
1893
|
+
},
|
|
1894
|
+
/**
|
|
1895
|
+
* Detach gesture listeners and cancel any active drag.
|
|
1896
|
+
*
|
|
1897
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1898
|
+
*/
|
|
1899
|
+
removeHooks() {
|
|
1294
1900
|
L.DomEvent.off(this._map._container, "wheel", this._handleShiftScroll, this);
|
|
1295
|
-
this.
|
|
1901
|
+
this.removePitchHooks();
|
|
1902
|
+
},
|
|
1903
|
+
/**
|
|
1904
|
+
* Listen for the start of Ctrl+drag pitch gestures.
|
|
1905
|
+
*
|
|
1906
|
+
* @since leaflet-rotate (0.5.0)
|
|
1907
|
+
*/
|
|
1908
|
+
addPitchHooks() {
|
|
1909
|
+
if (this._pitchHooksAdded) {
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
this._pitchEvents = L.version.startsWith("2.") ? ["pointerdown", "pointermove", "pointerup pointercancel"] : ["mousedown", "mousemove", "mouseup"];
|
|
1913
|
+
this._map._container.addEventListener(this._pitchEvents[0], this._onMouseDownBound, true);
|
|
1914
|
+
this._pitchHooksAdded = true;
|
|
1915
|
+
},
|
|
1916
|
+
/**
|
|
1917
|
+
* Stop listening for pitch gestures and remove active document listeners.
|
|
1918
|
+
*
|
|
1919
|
+
* @since leaflet-rotate (0.5.0)
|
|
1920
|
+
*/
|
|
1921
|
+
removePitchHooks() {
|
|
1922
|
+
if (!this._pitchHooksAdded) {
|
|
1923
|
+
return;
|
|
1924
|
+
}
|
|
1925
|
+
this._map._container.removeEventListener(this._pitchEvents[0], this._onMouseDownBound, true);
|
|
1926
|
+
this._onMouseUp();
|
|
1927
|
+
delete this._pitchEvents;
|
|
1928
|
+
this._pitchHooksAdded = false;
|
|
1296
1929
|
},
|
|
1297
|
-
|
|
1930
|
+
/**
|
|
1931
|
+
* Rotate the map in fixed increments while Shift is held during wheel input.
|
|
1932
|
+
*
|
|
1933
|
+
* @param {WheelEvent} e Wheel event from the map container.
|
|
1934
|
+
*
|
|
1935
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1936
|
+
*/
|
|
1937
|
+
_handleShiftScroll(e) {
|
|
1298
1938
|
if (e.shiftKey) {
|
|
1299
1939
|
e.preventDefault();
|
|
1300
1940
|
this._map.scrollWheelZoom.disable();
|
|
1301
|
-
this._map.setBearing(this._map.
|
|
1941
|
+
this._map.setBearing(this._map.getBearing() + Math.sign(e.deltaY) * 5);
|
|
1302
1942
|
} else {
|
|
1303
1943
|
this._map.scrollWheelZoom.enable();
|
|
1304
1944
|
}
|
|
1945
|
+
},
|
|
1946
|
+
/**
|
|
1947
|
+
* Start Ctrl+drag and capture subsequent movement outside the map.
|
|
1948
|
+
*
|
|
1949
|
+
* @param {MouseEvent|PointerEvent} e Gesture start event.
|
|
1950
|
+
*
|
|
1951
|
+
* @since leaflet-rotate (0.5.0)
|
|
1952
|
+
*/
|
|
1953
|
+
_onMouseDown(e) {
|
|
1954
|
+
if (!this._map._rotate || !this._map.options.pitchDrag || !e.ctrlKey || e.button !== 0) {
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
const scrollWheelZoom = this._map.scrollWheelZoom;
|
|
1958
|
+
if (scrollWheelZoom && scrollWheelZoom._timer) {
|
|
1959
|
+
clearTimeout(scrollWheelZoom._timer);
|
|
1960
|
+
scrollWheelZoom._timer = null;
|
|
1961
|
+
scrollWheelZoom._delta = 0;
|
|
1962
|
+
scrollWheelZoom._startTime = null;
|
|
1963
|
+
}
|
|
1964
|
+
e.preventDefault();
|
|
1965
|
+
e.stopPropagation();
|
|
1966
|
+
e.stopImmediatePropagation();
|
|
1967
|
+
this._lastX = e.clientX;
|
|
1968
|
+
this._lastY = e.clientY;
|
|
1969
|
+
document.addEventListener(this._pitchEvents[1], this._onMouseMoveBound);
|
|
1970
|
+
for (const event of this._pitchEvents[2].split(" ")) {
|
|
1971
|
+
document.addEventListener(event, this._onMouseUpBound);
|
|
1972
|
+
}
|
|
1973
|
+
},
|
|
1974
|
+
/**
|
|
1975
|
+
* Apply pointer movement to pitch (vertical) and bearing (horizontal).
|
|
1976
|
+
*
|
|
1977
|
+
* @param {MouseEvent|PointerEvent} e Gesture movement event.
|
|
1978
|
+
*
|
|
1979
|
+
* @since leaflet-rotate (0.5.0)
|
|
1980
|
+
*/
|
|
1981
|
+
_onMouseMove(e) {
|
|
1982
|
+
this._map._setPitchAndBearing(
|
|
1983
|
+
this._map.getPitch() + (this._lastY - e.clientY) * 0.5,
|
|
1984
|
+
this._map.getBearing() + (e.clientX - this._lastX) * 0.8
|
|
1985
|
+
);
|
|
1986
|
+
this._lastX = e.clientX;
|
|
1987
|
+
this._lastY = e.clientY;
|
|
1988
|
+
e.preventDefault();
|
|
1989
|
+
},
|
|
1990
|
+
/**
|
|
1991
|
+
* End Ctrl+drag and clear document-level listeners.
|
|
1992
|
+
*
|
|
1993
|
+
* @since leaflet-rotate (0.5.0)
|
|
1994
|
+
*/
|
|
1995
|
+
_onMouseUp() {
|
|
1996
|
+
if (!this._pitchEvents) {
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
document.removeEventListener(this._pitchEvents[1], this._onMouseMoveBound);
|
|
2000
|
+
for (const event of this._pitchEvents[2].split(" ")) {
|
|
2001
|
+
document.removeEventListener(event, this._onMouseUpBound);
|
|
2002
|
+
}
|
|
2003
|
+
delete this._lastX;
|
|
2004
|
+
delete this._lastY;
|
|
1305
2005
|
}
|
|
1306
2006
|
});
|
|
1307
|
-
L.Map.addInitHook("addHandler", "shiftKeyRotate", L.Map.ShiftKeyRotate);
|
|
1308
2007
|
L.Map.addInitHook(function() {
|
|
1309
|
-
if (this.
|
|
2008
|
+
if (this.options.shiftKeyRotate) {
|
|
2009
|
+
this.shiftKeyRotate = new L.Map.ShiftKeyRotate(this);
|
|
2010
|
+
this.shiftKeyRotate.enable();
|
|
2011
|
+
} else if (this.options.pitchDrag) {
|
|
2012
|
+
this.shiftKeyRotate = new L.Map.ShiftKeyRotate(this);
|
|
2013
|
+
this.shiftKeyRotate.addPitchHooks();
|
|
2014
|
+
this.on("unload", this.shiftKeyRotate.removePitchHooks, this.shiftKeyRotate);
|
|
2015
|
+
}
|
|
2016
|
+
if (this.scrollWheelZoom.enabled() && this.options.shiftKeyRotate) {
|
|
1310
2017
|
this.scrollWheelZoom.disable();
|
|
1311
2018
|
this.scrollWheelZoom.enable();
|
|
1312
2019
|
}
|
|
@@ -1322,6 +2029,8 @@ var leafletRotate = (() => {
|
|
|
1322
2029
|
* for touch-capable web browsers.
|
|
1323
2030
|
*
|
|
1324
2031
|
* @type {(Boolean|String)}
|
|
2032
|
+
*
|
|
2033
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1325
2034
|
*/
|
|
1326
2035
|
touchZoom: L.Browser.touch,
|
|
1327
2036
|
/**
|
|
@@ -1332,17 +2041,29 @@ var leafletRotate = (() => {
|
|
|
1332
2041
|
* when pinch-zooming.
|
|
1333
2042
|
*
|
|
1334
2043
|
* @type {Boolean}
|
|
2044
|
+
*
|
|
2045
|
+
* @since leaflet-rotate (v0.1.0)
|
|
1335
2046
|
*/
|
|
1336
2047
|
bounceAtZoomLimits: false
|
|
1337
2048
|
});
|
|
1338
2049
|
L.Map.TouchZoom = L.Handler.extend({
|
|
1339
|
-
|
|
1340
|
-
|
|
2050
|
+
/**
|
|
2051
|
+
* Enable pinch zoom handling and mark the shared touch handler as active.
|
|
2052
|
+
*
|
|
2053
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2054
|
+
*/
|
|
2055
|
+
addHooks() {
|
|
2056
|
+
this._map._container.classList.add("leaflet-touch-zoom");
|
|
1341
2057
|
this._map.touchGestures.enable();
|
|
1342
2058
|
this._map.touchGestures.zoom = true;
|
|
1343
2059
|
},
|
|
1344
|
-
|
|
1345
|
-
|
|
2060
|
+
/**
|
|
2061
|
+
* Disable pinch zoom handling while preserving touch rotation state.
|
|
2062
|
+
*
|
|
2063
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2064
|
+
*/
|
|
2065
|
+
removeHooks() {
|
|
2066
|
+
this._map._container.classList.remove("leaflet-touch-zoom");
|
|
1346
2067
|
this._map.touchGestures.zoom = false;
|
|
1347
2068
|
}
|
|
1348
2069
|
});
|
|
@@ -1354,9 +2075,18 @@ var leafletRotate = (() => {
|
|
|
1354
2075
|
position: "topleft",
|
|
1355
2076
|
closeOnZeroBearing: true
|
|
1356
2077
|
},
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
2078
|
+
/**
|
|
2079
|
+
* Create the control element and subscribe to map rotation.
|
|
2080
|
+
*
|
|
2081
|
+
* @param {L.Map} map
|
|
2082
|
+
*
|
|
2083
|
+
* @returns {HTMLElement} Control container.
|
|
2084
|
+
*
|
|
2085
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2086
|
+
*/
|
|
2087
|
+
onAdd(map) {
|
|
2088
|
+
const container = this._container = L.DomUtil.create("div", "leaflet-control-rotate leaflet-bar");
|
|
2089
|
+
const arrow = this._arrow = L.DomUtil.create("span", "leaflet-control-rotate-arrow");
|
|
1360
2090
|
arrow.style.backgroundImage = `url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E")`;
|
|
1361
2091
|
arrow.style.cursor = "grab";
|
|
1362
2092
|
arrow.style.display = "block";
|
|
@@ -1364,14 +2094,11 @@ var leafletRotate = (() => {
|
|
|
1364
2094
|
arrow.style.height = "100%";
|
|
1365
2095
|
arrow.style.backgroundRepeat = "no-repeat";
|
|
1366
2096
|
arrow.style.backgroundPosition = "50%";
|
|
1367
|
-
|
|
2097
|
+
const link = this._link = L.DomUtil.create("a", "leaflet-control-rotate-toggle", container);
|
|
1368
2098
|
link.appendChild(arrow);
|
|
1369
2099
|
link.href = "#";
|
|
1370
2100
|
link.title = "Rotate map";
|
|
1371
2101
|
L.DomEvent.on(link, "dblclick", L.DomEvent.stopPropagation).on(link, "mousedown", this._handleMouseDown, this).on(link, "click", L.DomEvent.stop).on(link, "click", this._cycleState, this).on(link, "click", this._refocusOnMap, this);
|
|
1372
|
-
if (!L.Browser.any3d) {
|
|
1373
|
-
L.DomUtil.addClass(link, "leaflet-disabled");
|
|
1374
|
-
}
|
|
1375
2102
|
this._restyle();
|
|
1376
2103
|
map.on("rotate", this._restyle, this);
|
|
1377
2104
|
this._follow = false;
|
|
@@ -1381,33 +2108,69 @@ var leafletRotate = (() => {
|
|
|
1381
2108
|
}
|
|
1382
2109
|
return container;
|
|
1383
2110
|
},
|
|
1384
|
-
|
|
2111
|
+
/**
|
|
2112
|
+
* Remove the map event listener when the control is detached.
|
|
2113
|
+
*
|
|
2114
|
+
* @param {L.Map} map
|
|
2115
|
+
*
|
|
2116
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2117
|
+
*/
|
|
2118
|
+
onRemove(map) {
|
|
1385
2119
|
map.off("rotate", this._restyle, this);
|
|
1386
2120
|
},
|
|
1387
|
-
|
|
2121
|
+
/**
|
|
2122
|
+
* Begin dragging the control's bearing indicator.
|
|
2123
|
+
*
|
|
2124
|
+
* @param {MouseEvent} e Mouse-down event.
|
|
2125
|
+
*
|
|
2126
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2127
|
+
*/
|
|
2128
|
+
_handleMouseDown(e) {
|
|
1388
2129
|
L.DomEvent.stop(e);
|
|
1389
2130
|
this.dragging = true;
|
|
1390
2131
|
this.dragstartX = e.pageX;
|
|
1391
2132
|
this.dragstartY = e.pageY;
|
|
1392
2133
|
L.DomEvent.on(document, "mousemove", this._handleMouseDrag, this).on(document, "mouseup", this._handleMouseUp, this);
|
|
1393
2134
|
},
|
|
1394
|
-
|
|
2135
|
+
/**
|
|
2136
|
+
* End bearing-indicator dragging and remove document listeners.
|
|
2137
|
+
*
|
|
2138
|
+
* @param {MouseEvent} e Mouse-up event.
|
|
2139
|
+
*
|
|
2140
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2141
|
+
*/
|
|
2142
|
+
_handleMouseUp(e) {
|
|
1395
2143
|
L.DomEvent.stop(e);
|
|
1396
2144
|
this.dragging = false;
|
|
1397
2145
|
L.DomEvent.off(document, "mousemove", this._handleMouseDrag, this).off(document, "mouseup", this._handleMouseUp, this);
|
|
1398
2146
|
},
|
|
1399
|
-
|
|
2147
|
+
/**
|
|
2148
|
+
* Set bearing from the horizontal drag distance.
|
|
2149
|
+
*
|
|
2150
|
+
* @param {MouseEvent} e Mouse-move event.
|
|
2151
|
+
*
|
|
2152
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2153
|
+
*/
|
|
2154
|
+
_handleMouseDrag(e) {
|
|
1400
2155
|
if (!this.dragging) {
|
|
1401
2156
|
return;
|
|
1402
2157
|
}
|
|
1403
|
-
|
|
2158
|
+
const deltaX = e.clientX - this.dragstartX;
|
|
1404
2159
|
this._map.setBearing(deltaX);
|
|
1405
2160
|
},
|
|
1406
|
-
|
|
2161
|
+
/**
|
|
2162
|
+
* Cycle through touch, compass, and locked rotation modes.
|
|
2163
|
+
* Compass mode may require permission from the browser.
|
|
2164
|
+
*
|
|
2165
|
+
* @param {Event} ev Control click event.
|
|
2166
|
+
*
|
|
2167
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2168
|
+
*/
|
|
2169
|
+
_cycleState(ev) {
|
|
1407
2170
|
if (!this._map) {
|
|
1408
2171
|
return;
|
|
1409
2172
|
}
|
|
1410
|
-
|
|
2173
|
+
const map = this._map;
|
|
1411
2174
|
if (!map.touchRotate.enabled() && !map.compassBearing.enabled()) {
|
|
1412
2175
|
map.touchRotate.enable();
|
|
1413
2176
|
} else if (!map.compassBearing.enabled()) {
|
|
@@ -1422,12 +2185,19 @@ var leafletRotate = (() => {
|
|
|
1422
2185
|
}
|
|
1423
2186
|
this._restyle();
|
|
1424
2187
|
},
|
|
1425
|
-
|
|
2188
|
+
/**
|
|
2189
|
+
* Update the control icon and visibility to match the current mode.
|
|
2190
|
+
*
|
|
2191
|
+
* @listens L.Map#rotate
|
|
2192
|
+
*
|
|
2193
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2194
|
+
*/
|
|
2195
|
+
_restyle() {
|
|
1426
2196
|
if (!this._map.options.rotate) {
|
|
1427
|
-
|
|
2197
|
+
this._link.classList.add("leaflet-disabled");
|
|
1428
2198
|
} else {
|
|
1429
|
-
|
|
1430
|
-
|
|
2199
|
+
const map = this._map;
|
|
2200
|
+
const bearing = map.getBearing();
|
|
1431
2201
|
this._arrow.style.transform = "rotate(" + bearing + "deg)";
|
|
1432
2202
|
if (bearing && this.options.closeOnZeroBearing) {
|
|
1433
2203
|
this._container.style.display = "block";
|
|
@@ -1445,16 +2215,24 @@ var leafletRotate = (() => {
|
|
|
1445
2215
|
}
|
|
1446
2216
|
}
|
|
1447
2217
|
});
|
|
1448
|
-
L.control
|
|
1449
|
-
|
|
1450
|
-
}
|
|
2218
|
+
if (L.control) {
|
|
2219
|
+
L.control.rotate = (options) => new L.Control.Rotate(options);
|
|
2220
|
+
}
|
|
1451
2221
|
L.Map.mergeOptions({
|
|
2222
|
+
/**
|
|
2223
|
+
* Add the rotation control automatically. Set to `false` to disable,
|
|
2224
|
+
* or pass an options object to configure the control.
|
|
2225
|
+
*
|
|
2226
|
+
* @type {boolean|Object}
|
|
2227
|
+
*
|
|
2228
|
+
* @since leaflet-rotate (v0.1.0)
|
|
2229
|
+
*/
|
|
1452
2230
|
rotateControl: true
|
|
1453
2231
|
});
|
|
1454
2232
|
L.Map.addInitHook(function() {
|
|
1455
2233
|
if (this.options.rotateControl) {
|
|
1456
|
-
|
|
1457
|
-
this.rotateControl = L.
|
|
2234
|
+
const options = typeof this.options.rotateControl === "object" ? this.options.rotateControl : {};
|
|
2235
|
+
this.rotateControl = new L.Control.Rotate(options);
|
|
1458
2236
|
this.addControl(this.rotateControl);
|
|
1459
2237
|
}
|
|
1460
2238
|
});
|