leaflet-rotate 0.2.7 → 0.3.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.
@@ -1,1936 +1,1463 @@
1
- (function (factory) {
2
- typeof define === 'function' && define.amd ? define(factory) :
3
- factory();
4
- })((function () { 'use strict';
5
-
1
+ var leafletRotate = (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/index.js
17
+ var index_exports = {};
18
+
19
+ // src/dom/DomUtil.js
20
+ var domUtilProto = L.extend({}, L.DomUtil);
21
+ L.extend(L.DomUtil, {
6
22
  /**
7
- * @external L.DomUtil
23
+ * Resets the 3D CSS transform of `el` so it is
24
+ * translated by `offset` pixels and optionally
25
+ * scaled by `scale`. Does not have an effect if
26
+ * the browser doesn't support 3D CSS transforms.
8
27
  *
9
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/dom/DomUtil.js
28
+ * @param {HTMLElement} el
29
+ * @param {L.Point} offset
30
+ * @param {Number} scale
31
+ * @param {Number} bearing
32
+ * @param {L.Point} pivot
10
33
  */
11
-
12
- const domUtilProto = L.extend({}, L.DomUtil);
13
-
14
- L.extend(L.DomUtil, {
15
-
16
- /**
17
- * Resets the 3D CSS transform of `el` so it is
18
- * translated by `offset` pixels and optionally
19
- * scaled by `scale`. Does not have an effect if
20
- * the browser doesn't support 3D CSS transforms.
21
- *
22
- * @param {HTMLElement} el
23
- * @param {L.Point} offset
24
- * @param {Number} scale
25
- * @param {Number} bearing
26
- * @param {L.Point} pivot
27
- */
28
- setTransform: function(el, offset, scale, bearing, pivot) {
29
- var pos = offset || new L.Point(0, 0);
30
-
31
- if (!bearing) {
32
- offset = pos._round();
33
- return domUtilProto.setTransform.apply(this, arguments);
34
- }
35
-
36
- pos = pos.rotateFrom(bearing, pivot);
37
-
38
- el.style[L.DomUtil.TRANSFORM] =
39
- 'translate3d(' + pos.x + 'px,' + pos.y + 'px' + ',0)' +
40
- (scale ? ' scale(' + scale + ')' : '') +
41
- ' rotate(' + bearing + 'rad)';
42
- },
43
-
44
- /**
45
- * Sets the position of `el` to coordinates specified by
46
- * `position`, using CSS translate or top/left positioning
47
- * depending on the browser (used by Leaflet internally
48
- * to position its layers).
49
- *
50
- * @param {HTMLElement} el
51
- * @param {L.Point} point
52
- * @param {Number} bearing
53
- * @param {L.Point} pivot
54
- * @param {Number} scale
55
- */
56
- setPosition: function(el, point, bearing, pivot, scale) {
57
- if (!bearing) {
58
- return domUtilProto.setPosition.apply(this, arguments);
59
- }
60
-
61
- /*eslint-disable */
62
- el._leaflet_pos = point;
63
- /*eslint-enable */
64
-
65
- if (L.Browser.any3d) {
66
- L.DomUtil.setTransform(el, point, scale, bearing, pivot);
67
- } else {
68
- el.style.left = point.x + 'px';
69
- el.style.top = point.y + 'px';
70
- }
71
- },
72
-
73
- /**
74
- * @constant radians = degrees × π/180°
75
- */
76
- DEG_TO_RAD: Math.PI / 180,
77
-
78
- /**
79
- * @constant degrees = radians × 180°/π
80
- */
81
- RAD_TO_DEG: 180 / Math.PI,
82
-
83
- });
84
-
34
+ setTransform: function(el, offset, scale, bearing, pivot) {
35
+ var pos = offset || new L.Point(0, 0);
36
+ if (!bearing) {
37
+ offset = pos._round();
38
+ return domUtilProto.setTransform.apply(this, arguments);
39
+ }
40
+ pos = pos.rotateFrom(bearing, pivot);
41
+ el.style[L.DomUtil.TRANSFORM] = "translate3d(" + pos.x + "px," + pos.y + "px,0)" + (scale ? " scale(" + scale + ")" : "") + " rotate(" + bearing + "rad)";
42
+ },
85
43
  /**
86
- * @external L.Draggable
44
+ * Sets the position of `el` to coordinates specified by
45
+ * `position`, using CSS translate or top/left positioning
46
+ * depending on the browser (used by Leaflet internally
47
+ * to position its layers).
87
48
  *
88
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/dom/Draggable.js
49
+ * @param {HTMLElement} el
50
+ * @param {L.Point} point
51
+ * @param {Number} bearing
52
+ * @param {L.Point} pivot
53
+ * @param {Number} scale
89
54
  */
90
-
55
+ setPosition: function(el, point, bearing, pivot, scale) {
56
+ if (!bearing) {
57
+ return domUtilProto.setPosition.apply(this, arguments);
58
+ }
59
+ el._leaflet_pos = point;
60
+ if (L.Browser.any3d) {
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
+ }
66
+ },
91
67
  /**
92
- * A class for making DOM elements draggable (including touch support).
93
- * Used internally for map and marker dragging. Only works for elements
94
- * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
68
+ * @constant radians = degrees × π/180°
95
69
  */
96
-
97
- L.Draggable.include({
98
-
99
- /** @TODO */
100
- // updateMapBearing: function(mapBearing) {
101
- // this._mapBearing = mapBearing;
102
- // },
103
-
104
- });
105
-
70
+ DEG_TO_RAD: Math.PI / 180,
106
71
  /**
107
- * @external L.Point
72
+ * @constant degrees = radians × 180°/π
73
+ */
74
+ RAD_TO_DEG: 180 / Math.PI
75
+ });
76
+
77
+ // src/dom/Draggable.js
78
+ L.Draggable.include({
79
+ /** @TODO */
80
+ // updateMapBearing: function(mapBearing) {
81
+ // this._mapBearing = mapBearing;
82
+ // },
83
+ });
84
+
85
+ // src/geometry/Point.js
86
+ L.extend(L.Point.prototype, {
87
+ /**
88
+ * Rotate around (0,0) by applying the 2D rotation matrix:
89
+ *
90
+ * ⎡ x' ⎤ = ⎡ cos θ -sin θ ⎤ ⎡ x ⎤
91
+ * ⎣ y' ⎦ ⎣ sin θ cos θ ⎦ ⎣ y ⎦
108
92
  *
109
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/geometry/Point.js
93
+ * @param theta must be given in radians.
110
94
  */
111
-
112
- L.extend(L.Point.prototype, {
113
-
114
- /**
115
- * Rotate around (0,0) by applying the 2D rotation matrix:
116
- *
117
- * ⎡ x' ⎤ = ⎡ cos θ -sin θ ⎤ ⎡ x ⎤
118
- * ⎣ y' ⎦ ⎣ sin θ cos θ ⎦ ⎣ y ⎦
119
- *
120
- * @param theta must be given in radians.
121
- */
122
- rotate: function(theta) {
123
- return this.rotateFrom(theta, new L.Point(0,0))
124
- },
125
-
126
- /**
127
- * Rotate around (pivot.x, pivot.y) by:
128
- *
129
- * 1. subtract (pivot.x, pivot.y)
130
- * 2. rotate around (0, 0)
131
- * 3. add (pivot.x, pivot.y) back
132
- *
133
- * same as `this.subtract(pivot).rotate(theta).add(pivot)`
134
- *
135
- * @param {Number} theta
136
- * @param {L.Point} pivot
137
- *
138
- * @returns {L.Point}
139
- */
140
- rotateFrom: function(theta, pivot) {
141
- if (!theta) { return this; }
142
- var sinTheta = Math.sin(theta);
143
- var cosTheta = Math.cos(theta);
144
- var cx = pivot.x,
145
- cy = pivot.y;
146
- var x = this.x - cx,
147
- y = this.y - cy;
148
-
149
- return new L.Point(
150
- x * cosTheta - y * sinTheta + cx,
151
- x * sinTheta + y * cosTheta + cy
152
- );
153
- },
154
-
155
- });
156
-
95
+ rotate: function(theta) {
96
+ return this.rotateFrom(theta, new L.Point(0, 0));
97
+ },
157
98
  /**
158
- * @external L.DivOverlay
99
+ * Rotate around (pivot.x, pivot.y) by:
100
+ *
101
+ * 1. subtract (pivot.x, pivot.y)
102
+ * 2. rotate around (0, 0)
103
+ * 3. add (pivot.x, pivot.y) back
159
104
  *
160
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/DivOverlay.js
105
+ * same as `this.subtract(pivot).rotate(theta).add(pivot)`
106
+ *
107
+ * @param {Number} theta
108
+ * @param {L.Point} pivot
109
+ *
110
+ * @returns {L.Point}
161
111
  */
162
-
163
- const divOverlayProto = L.extend({}, L.DivOverlay.prototype);
164
-
165
- L.DivOverlay.include({
166
-
167
- /**
168
- * Update L.Popup and L.Tooltip anchor positions after
169
- * the map is moved by calling `map.setBearing(theta)`
170
- *
171
- * @listens L.Map~rotate
172
- */
173
- getEvents: function() {
174
- return L.extend(divOverlayProto.getEvents.apply(this, arguments), { rotate: this._updatePosition });
175
- },
176
-
177
- /**
178
- * 0. update element anchor point (divOverlayProto v1.9.3)
179
- * 1. rotate around anchor point (subtract anchor -> rotate point -> add anchor)
180
- */
181
- _updatePosition: function() {
182
- if (!this._map) { return; }
183
- divOverlayProto._updatePosition.apply(this, arguments);
184
- if (this._map && this._map._rotate && this._zoomAnimated) {
185
- var anchor = this._getAnchor();
186
- var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
187
- L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
188
- }
189
-
190
- },
191
-
192
- });
193
-
112
+ rotateFrom: function(theta, pivot) {
113
+ if (!theta) {
114
+ return this;
115
+ }
116
+ var sinTheta = Math.sin(theta);
117
+ var cosTheta = Math.cos(theta);
118
+ var cx = pivot.x, cy = pivot.y;
119
+ var x = this.x - cx, y = this.y - cy;
120
+ return new L.Point(
121
+ x * cosTheta - y * sinTheta + cx,
122
+ x * sinTheta + y * cosTheta + cy
123
+ );
124
+ }
125
+ });
126
+
127
+ // src/layer/DivOverlay.js
128
+ var divOverlayProto = L.extend({}, L.DivOverlay.prototype);
129
+ L.DivOverlay.include({
194
130
  /**
195
- * @external L.Popup
131
+ * Update L.Popup and L.Tooltip anchor positions after
132
+ * the map is moved by calling `map.setBearing(theta)`
196
133
  *
197
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/Popup.js
134
+ * @listens L.Map~rotate
198
135
  */
199
-
200
- const popupProto = L.extend({}, L.Popup.prototype);
201
-
202
- L.Popup.include({
203
-
204
- /**
205
- * 0. update element anchor point (popupProto v1.9.3)
206
- * 1. rotate around anchor point (subtract anchor -> rotate point -> add anchor)
207
- */
208
- _animateZoom: function(e) {
209
- popupProto._animateZoom.apply(this, arguments);
210
- if (this._map && this._map._rotate) {
211
- var anchor = this._getAnchor();
212
- var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
213
- L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
214
- }
215
- },
216
-
217
- /**
218
- * Fix for L.popup({ keepInView = true })
219
- *
220
- * @see https://github.com/fnicollet/Leaflet/pull/21
221
- */
222
- _adjustPan: function() {
223
- if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
224
-
225
- // We can endlessly recurse if keepInView is set and the view resets.
226
- // Let's guard against that by exiting early if we're responding to our own autopan.
227
- if (this._autopanning) {
228
- this._autopanning = false;
229
- return;
230
- }
231
-
232
- var map = this._map,
233
- marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
234
- containerHeight = this._container.offsetHeight + marginBottom,
235
- containerWidth = this._containerWidth,
236
- layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
237
-
238
- layerPos._add(L.DomUtil.getPosition(this._container));
239
-
240
- /** @TODO use popupProto._adjustPan */
241
- // var containerPos = map.layerPointToContainerPoint(layerPos);
242
- var containerPos = layerPos._add(this._map._getMapPanePos()),
243
- padding = L.point(this.options.autoPanPadding),
244
- paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
245
- paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
246
- size = map.getSize(),
247
- dx = 0,
248
- dy = 0;
249
-
250
- if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
251
- dx = containerPos.x + containerWidth - size.x + paddingBR.x;
252
- }
253
- if (containerPos.x - dx - paddingTL.x < 0) { // left
254
- dx = containerPos.x - paddingTL.x;
255
- }
256
- if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
257
- dy = containerPos.y + containerHeight - size.y + paddingBR.y;
258
- }
259
- if (containerPos.y - dy - paddingTL.y < 0) { // top
260
- dy = containerPos.y - paddingTL.y;
261
- }
262
-
263
- // @namespace Map
264
- // @section Popup events
265
- // @event autopanstart: Event
266
- // Fired when the map starts autopanning when opening a popup.
267
- if (dx || dy) {
268
- // Track that we're autopanning, as this function will be re-ran on moveend
269
- if (this.options.keepInView) {
270
- this._autopanning = true;
271
- }
272
- map
273
- .fire('autopanstart')
274
- .panBy([dx, dy]);
275
- }
276
- },
277
-
278
- });
279
-
136
+ getEvents: function() {
137
+ return L.extend(divOverlayProto.getEvents.apply(this, arguments), { rotate: this._updatePosition });
138
+ },
280
139
  /**
281
- * @external L.Tooltip
140
+ * 0. update element anchor point (divOverlayProto v1.9.3)
141
+ * 1. rotate around anchor point (subtract anchor -> rotate point -> add anchor)
142
+ */
143
+ _updatePosition: function() {
144
+ if (!this._map) {
145
+ return;
146
+ }
147
+ divOverlayProto._updatePosition.apply(this, arguments);
148
+ if (this._map._rotate && this._zoomAnimated) {
149
+ var anchor = this._getAnchor();
150
+ var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
151
+ L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
152
+ } else if (this._map._rotate) {
153
+ var mapPanePoint = this._map.rotatedPointToMapPanePoint(this._map.latLngToLayerPoint(this._latlng)), offset = L.point(this.options.offset), anchor = this._getAnchor();
154
+ this._containerLeft = -Math.round(this._containerWidth / 2) + mapPanePoint.x + offset.x + anchor.x;
155
+ this._containerBottom = -(mapPanePoint.y + offset.y + anchor.y);
156
+ this._container.style.left = this._containerLeft + "px";
157
+ this._container.style.bottom = this._containerBottom + "px";
158
+ this._container.style.top = "";
159
+ L.DomUtil.setPosition(this._container, new L.Point(0, 0));
160
+ this._container.style.transform = "";
161
+ }
162
+ }
163
+ });
164
+
165
+ // src/layer/Popup.js
166
+ var popupProto = L.extend({}, L.Popup.prototype);
167
+ L.Popup.include({
168
+ /**
169
+ * 0. update element anchor point (popupProto v1.9.3)
170
+ * 1. rotate around anchor point (subtract anchor -> rotate point -> add anchor)
171
+ */
172
+ _animateZoom: function(e) {
173
+ popupProto._animateZoom.apply(this, arguments);
174
+ if (this._map && this._map._rotate) {
175
+ var anchor = this._getAnchor();
176
+ var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
177
+ L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
178
+ }
179
+ },
180
+ /**
181
+ * Fix for L.popup({ keepInView = true })
282
182
  *
283
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/Tooltip.js
183
+ * @see https://github.com/fnicollet/Leaflet/pull/21
284
184
  */
285
-
286
- const tooltipProto = L.extend({}, L.Tooltip.prototype);
287
-
288
- L.Tooltip.include({
289
-
290
- _animateZoom: function(e) {
291
- if (!this._map._rotate) {
292
- return tooltipProto._animateZoom.apply(this, arguments);
293
- }
294
- var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
295
-
296
- pos = this._map.rotatedPointToMapPanePoint(pos);
297
- this._setPosition(pos);
298
- },
299
-
300
- _updatePosition: function() {
301
- if (!this._map._rotate) {
302
- return tooltipProto._updatePosition.apply(this, arguments);
303
- }
304
- var pos = this._map.latLngToLayerPoint(this._latlng);
305
-
306
- pos = this._map.rotatedPointToMapPanePoint(pos);
307
- this._setPosition(pos);
308
- },
309
-
310
- });
311
-
185
+ _adjustPan: function() {
186
+ if (!this.options.autoPan || this._map._panAnim && this._map._panAnim._inProgress) {
187
+ return;
188
+ }
189
+ if (this._autopanning) {
190
+ this._autopanning = false;
191
+ return;
192
+ }
193
+ var map = this._map, marginBottom = parseInt(L.DomUtil.getStyle(this._container, "marginBottom"), 10) || 0, containerHeight = this._container.offsetHeight + marginBottom, containerWidth = this._containerWidth, layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
194
+ layerPos._add(L.DomUtil.getPosition(this._container));
195
+ var containerPos = layerPos._add(this._map._getMapPanePos()), padding = L.point(this.options.autoPanPadding), paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding), paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding), size = map.getSize(), dx = 0, dy = 0;
196
+ if (containerPos.x + containerWidth + paddingBR.x > size.x) {
197
+ dx = containerPos.x + containerWidth - size.x + paddingBR.x;
198
+ }
199
+ if (containerPos.x - dx - paddingTL.x < 0) {
200
+ dx = containerPos.x - paddingTL.x;
201
+ }
202
+ if (containerPos.y + containerHeight + paddingBR.y > size.y) {
203
+ dy = containerPos.y + containerHeight - size.y + paddingBR.y;
204
+ }
205
+ if (containerPos.y - dy - paddingTL.y < 0) {
206
+ dy = containerPos.y - paddingTL.y;
207
+ }
208
+ if (dx || dy) {
209
+ if (this.options.keepInView) {
210
+ this._autopanning = true;
211
+ }
212
+ map.fire("autopanstart").panBy([dx, dy]);
213
+ }
214
+ }
215
+ });
216
+
217
+ // src/layer/Tooltip.js
218
+ var tooltipProto = L.extend({}, L.Tooltip.prototype);
219
+ L.Tooltip.include({
220
+ _animateZoom: function(e) {
221
+ if (!this._map._rotate) {
222
+ return tooltipProto._animateZoom.apply(this, arguments);
223
+ }
224
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
225
+ pos = this._map.rotatedPointToMapPanePoint(pos);
226
+ this._setPosition(pos);
227
+ },
228
+ _updatePosition: function() {
229
+ if (!this._map._rotate) {
230
+ return tooltipProto._updatePosition.apply(this, arguments);
231
+ }
232
+ var pos = this._map.latLngToLayerPoint(this._latlng);
233
+ pos = this._map.rotatedPointToMapPanePoint(pos);
234
+ this._setPosition(pos);
235
+ }
236
+ });
237
+
238
+ // src/layer/marker/Icon.js
239
+ var iconProto = L.extend({}, L.Icon.prototype);
240
+ L.Icon.include({
241
+ _setIconStyles: function(img, name) {
242
+ var options = this.options;
243
+ var sizeOption = options[name + "Size"];
244
+ if (typeof sizeOption === "number") {
245
+ sizeOption = [sizeOption, sizeOption];
246
+ }
247
+ var size = L.point(sizeOption), anchor = L.point(name === "shadow" && options.shadowAnchor || options.iconAnchor || size && size.divideBy(2, true));
248
+ img.className = "leaflet-marker-" + name + " " + (options.className || "");
249
+ if (anchor) {
250
+ img.style.marginLeft = -anchor.x + "px";
251
+ img.style.marginTop = -anchor.y + "px";
252
+ img.style[L.DomUtil.TRANSFORM + "Origin"] = anchor.x + "px " + anchor.y + "px 0px";
253
+ }
254
+ if (size) {
255
+ img.style.width = size.x + "px";
256
+ img.style.height = size.y + "px";
257
+ }
258
+ }
259
+ });
260
+
261
+ // src/layer/marker/Marker.js
262
+ var markerProto = L.extend({}, L.Marker.prototype);
263
+ L.Marker.mergeOptions({
312
264
  /**
313
- * @external L.Icon
265
+ * Rotation of this marker in rad
314
266
  *
315
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Icon.js
267
+ * @type {Number}
316
268
  */
317
-
318
- L.extend({}, L.Icon.prototype);
319
-
320
- L.Icon.include({
321
-
322
- _setIconStyles: function(img, name) {
323
- var options = this.options;
324
- var sizeOption = options[name + 'Size'];
325
-
326
- if (typeof sizeOption === 'number') {
327
- sizeOption = [sizeOption, sizeOption];
328
- }
329
-
330
- var size = L.point(sizeOption),
331
- anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
332
- size && size.divideBy(2, true));
333
-
334
- img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
335
-
336
- if (anchor) {
337
- img.style.marginLeft = (-anchor.x) + 'px';
338
- img.style.marginTop = (-anchor.y) + 'px';
339
- /** @TODO use iconProto._setIconStyles */
340
- img.style[L.DomUtil.TRANSFORM + "Origin"] = anchor.x + "px " + anchor.y + "px 0px";
341
- }
342
-
343
- if (size) {
344
- img.style.width = size.x + 'px';
345
- img.style.height = size.y + 'px';
346
- }
347
- },
348
-
349
- });
350
-
269
+ rotation: 0,
351
270
  /**
352
- * @external L.Marker
353
- * @external L.Handler.MarkerDrag
271
+ * Rotate this marker when map rotates
354
272
  *
355
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Marker.js
356
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Marker.Drag.js
357
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/dom/Draggable.js
273
+ * @type {Boolean}
358
274
  */
359
-
360
- const markerProto = L.extend({}, L.Marker.prototype);
361
-
362
- L.Marker.mergeOptions({
363
-
364
- /**
365
- * Rotation of this marker in rad
366
- *
367
- * @type {Number}
368
- */
369
- rotation: 0,
370
-
371
- /**
372
- * Rotate this marker when map rotates
373
- *
374
- * @type {Boolean}
375
- */
376
- rotateWithView: false,
377
-
378
- /**
379
- * Scale of the marker icon
380
- *
381
- * @type {Number}
382
- */
383
- scale: undefined,
384
-
385
- });
386
-
387
- var markerDragProto; // retrived at runtime (see below: L.Marker::_initInteraction())
388
-
389
- var MarkerDrag = {
390
-
391
- // _onDragStart: function() {
392
- // if (!this._marker._map._rotate) {
393
- // return markerDragProto._onDragStart.apply(this, arguments);
394
- // }
395
- // this._draggable.updateMapBearing(this._marker._map._bearing);
396
- // },
397
-
398
- _onDrag: function(e) {
399
- var marker = this._marker,
400
- /** @TODO use markerDragProto._onDrag */
401
- rotated_marker = marker.options.rotation || marker.options.rotateWithView,
402
- shadow = marker._shadow,
403
- iconPos = L.DomUtil.getPosition(marker._icon);
404
-
405
- /** @TODO use markerDragProto._onDrag */
406
- // update shadow position
407
- if (!rotated_marker && shadow) {
408
- L.DomUtil.setPosition(shadow, iconPos);
409
- }
410
-
411
- /** @TODO use markerDragProto._onDrag */
412
- if (marker._map._rotate) {
413
- // Reverse calculation from mapPane coordinates to rotatePane coordinates
414
- iconPos = marker._map.mapPanePointToRotatedPoint(iconPos);
415
- }
416
- var latlng = marker._map.layerPointToLatLng(iconPos);
417
-
418
- marker._latlng = latlng;
419
- e.latlng = latlng;
420
- e.oldLatLng = this._oldLatLng;
421
-
422
- /** @TODO use markerDragProto._onDrag */
423
- if (rotated_marker) marker.setLatLng(latlng); // use `setLatLng` to presisit rotation. low efficiency
424
- else marker.fire('move', e); // `setLatLng` will trig 'move' event. we imitate here.
425
-
426
- // @event drag: Event
427
- // Fired repeatedly while the user drags the marker.
428
- marker
429
- .fire('drag', e);
430
- },
431
-
432
- _onDragEnd: function(e) {
433
- if (this._marker._map._rotate) {
434
- this._marker.update();
435
- }
436
- markerDragProto._onDragEnd.apply(this, arguments);
437
- },
438
-
439
- };
440
-
441
- L.Marker.include({
442
-
443
- /**
444
- * Update L.Marker anchor position after the map
445
- * is moved by calling `map.setBearing(theta)`
446
- *
447
- * @listens L.Map~rotate
448
- */
449
- getEvents: function() {
450
- return L.extend(markerProto.getEvents.apply(this, arguments), { rotate: this.update });
451
- },
452
-
453
- _initInteraction: function() {
454
- var ret = markerProto._initInteraction.apply(this, arguments);
455
- if (this.dragging && this.dragging.enabled() && this._map && this._map._rotate) {
456
- // L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable
457
- markerDragProto = markerDragProto || Object.getPrototypeOf(this.dragging);
458
- Object.assign(this.dragging, {
459
- // _onDragStart: MarkerDrag._onDragStart.bind(this.dragging),
460
- _onDrag: MarkerDrag._onDrag.bind(this.dragging),
461
- _onDragEnd: MarkerDrag._onDragEnd.bind(this.dragging),
462
- });
463
- this.dragging.disable();
464
- this.dragging.enable();
465
- }
466
- return ret;
467
- },
468
-
469
- _setPos: function(pos) {
470
-
471
- /** @TODO use markerProto._setPos */
472
- if (this._map._rotate) {
473
- pos = this._map.rotatedPointToMapPanePoint(pos);
474
- }
475
-
476
- /** @TODO use markerProto._setPos */
477
- var bearing = this.options.rotation || 0;
478
- if (this.options.rotateWithView) {
479
- bearing += this._map._bearing;
480
- }
481
-
482
- /** @TODO use markerProto._setPos */
483
- if (this._icon) {
484
- L.DomUtil.setPosition(this._icon, pos, bearing, pos, this.options.scale);
485
- }
486
-
487
- /** @TODO use markerProto._setPos */
488
- if (this._shadow) {
489
- L.DomUtil.setPosition(this._shadow, pos, bearing, pos, this.options.scale);
490
- }
491
-
492
- this._zIndex = pos.y + this.options.zIndexOffset;
493
-
494
- this._resetZIndex();
495
- },
496
-
497
- // _updateZIndex: function(offset) {
498
- // if (!this._map._rotate) {
499
- // return markerProto._updateZIndex.apply(this, arguments);
500
- // }
501
- // this._icon.style.zIndex = Math.round(this._zIndex + offset);
502
- // },
503
-
504
- setRotation: function(rotation) {
505
- this.options.rotation = rotation;
506
- this.update();
507
- },
508
-
509
- });
510
-
275
+ rotateWithView: false,
276
+ /**
277
+ * Flip a rotated marker when needed to keep it upright
278
+ *
279
+ * @type {Boolean}
280
+ */
281
+ keepUpright: false,
511
282
  /**
512
- * @external L.GridLayer
283
+ * Scale of the marker icon
513
284
  *
514
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/tile/GridLayer.js
285
+ * @type {Number}
515
286
  */
516
-
517
- const gridLayerProto = L.extend({}, L.GridLayer.prototype);
518
-
519
- L.GridLayer.include({
520
-
521
- /**
522
- * Redraw L.TileLayer bounds after the map is
523
- * moved by just calling `map.setBearing(theta)`
524
- *
525
- * @listens L.Map~rotate
526
- */
527
- getEvents: function() {
528
- var events = gridLayerProto.getEvents.apply(this, arguments);
529
- if (this._map._rotate && !this.options.updateWhenIdle) {
530
- if (!this._onRotate) {
531
- this._onRotate = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
532
- }
533
- events.rotate = this._onRotate;
534
- }
535
- return events;
536
- },
537
-
538
- _getTiledPixelBounds: function(center) {
539
- if (!this._map._rotate) {
540
- return gridLayerProto._getTiledPixelBounds.apply(this, arguments);
541
- }
542
-
543
- return this._map._getNewPixelBounds(center, this._tileZoom);
544
- },
545
-
546
- });
547
-
287
+ scale: void 0
288
+ });
289
+ var markerDragProto;
290
+ var MarkerDrag = {
291
+ // _onDragStart: function() {
292
+ // if (!this._marker._map._rotate) {
293
+ // return markerDragProto._onDragStart.apply(this, arguments);
294
+ // }
295
+ // this._draggable.updateMapBearing(this._marker._map._bearing);
296
+ // },
297
+ _onDrag: function(e) {
298
+ var marker = this._marker, rotated_marker = marker.options.rotation || marker.options.rotateWithView, shadow = marker._shadow, iconPos = L.DomUtil.getPosition(marker._icon);
299
+ if (!rotated_marker && shadow) {
300
+ L.DomUtil.setPosition(shadow, iconPos);
301
+ }
302
+ if (marker._map._rotate) {
303
+ iconPos = marker._map.mapPanePointToRotatedPoint(iconPos);
304
+ }
305
+ var latlng = marker._map.layerPointToLatLng(iconPos);
306
+ marker._latlng = latlng;
307
+ e.latlng = latlng;
308
+ e.oldLatLng = this._oldLatLng;
309
+ if (rotated_marker) marker.setLatLng(latlng);
310
+ else marker.fire("move", e);
311
+ marker.fire("drag", e);
312
+ },
313
+ _onDragEnd: function(e) {
314
+ if (this._marker._map._rotate) {
315
+ this._marker.update();
316
+ }
317
+ markerDragProto._onDragEnd.apply(this, arguments);
318
+ }
319
+ };
320
+ L.Marker.include({
548
321
  /**
549
- * @external L.Renderer
322
+ * Update L.Marker anchor position after the map
323
+ * is moved by calling `map.setBearing(theta)`
550
324
  *
551
- * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/vector/Renderer.js
325
+ * @listens L.Map~rotate
552
326
  */
553
-
554
- const rendererProto = L.extend({}, L.Renderer.prototype);
555
-
556
- L.Renderer.include({
557
-
558
- /**
559
- * Redraw L.Canvas and L.SVG renderer bounds after the
560
- * map is moved by just calling `map.setBearing(theta)`
561
- *
562
- * @listens L.Map~rotate
563
- */
564
- getEvents: function() {
565
- return L.extend(rendererProto.getEvents.apply(this, arguments), { rotate: this._update });
566
- },
567
-
568
- /**
569
- * Fix for `map.flyTo()` when `false === map.options.zoomAnimation`
570
- *
571
- * @see https://github.com/Leaflet/Leaflet/pull/8794
572
- */
573
- onAdd: function() {
574
- rendererProto.onAdd.apply(this, arguments);
575
- if (L.version <= "1.9.3") {
576
- // always keep transform-origin as 0 0
577
- this._container.classList.add('leaflet-zoom-animated');
578
- }
579
- },
580
-
581
- /**
582
- * @FIXME layer drifts on `map.setZoom()` (eg. zoom during animation)
583
- *
584
- * the main cause seems to be related to `this._updateTransform(path._center, path._zoom))`
585
- * and `this._topLeft = this._map.layerPointToLatLng(this._bounds.min);`
586
- *
587
- * @example
588
- * map.setZoom(2);
589
- * path._renderer._update();
590
- * path._renderer._updateTransform(path._renderer._center, path._renderer._zoom);
591
- *
592
- * @see https://github.com/Leaflet/Leaflet/pull/8794
593
- * @see https://github.com/Leaflet/Leaflet/pull/8103
594
- * @see https://github.com/Leaflet/Leaflet/issues/7466
595
- *
596
- * @TODO rechek this changes from leaflet@v1.9.3
597
- *
598
- * @see https://github.com/Leaflet/Leaflet/compare/v1.7.0...v1.9.3
599
- */
600
- _updateTransform: function(center, zoom) {
601
- if (!this._map._rotate) {
602
- return rendererProto._updateTransform.apply(this, arguments);
603
- }
604
- /**
605
- * @FIXME see path._renderer._reset();
606
- */
607
- var scale = this._map.getZoomScale(zoom, this._zoom),
608
- offset = this._map._latLngToNewLayerPoint(this._topLeft, zoom, center);
609
-
610
- L.DomUtil.setTransform(this._container, offset, scale);
611
-
612
- },
613
-
614
- // getEvents() {
615
- // const events = {
616
- // viewreset: this._reset,
617
- // zoom: this._onZoom,
618
- // moveend: this._update,
619
- // zoomend: this._onZoomEnd
620
- // };
621
- // if (this._zoomAnimated) {
622
- // events.zoomanim = this._onAnimZoom;
623
- // }
624
- // return events;
625
- // },
626
-
627
- // _onAnimZoom(ev) {
628
- // this._updateTransform(ev.center, ev.zoom);
629
- // },
630
-
631
- // _onZoom() {
632
- // this._updateTransform(this._map.getCenter(), this._map.getZoom());
633
- // },
634
-
635
- // _onZoomEnd() {
636
- // for (const id in this._layers) {
637
- // this._layers[id]._project();
638
- // }
639
- // },
640
-
641
- // _reset() {
642
- // this._update();
643
- // this._updateTransform(this._center, this._zoom);
644
-
645
- // for (const id in this._layers) {
646
- // this._layers[id]._reset();
647
- // }
648
- // },
649
-
650
- // _updatePaths() {
651
- // for (const id in this._layers) {
652
- // this._layers[id]._update();
653
- // }
654
- // },
655
-
656
- _update: function() {
657
- if (!this._map._rotate) {
658
- return rendererProto._update.apply(this, arguments);
659
- }
660
- // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
661
- // Subclasses are responsible of firing the 'update' event.
662
- this._bounds = this._map._getPaddedPixelBounds(this.options.padding);
663
- this._topLeft = this._map.layerPointToLatLng(this._bounds.min);
664
- this._center = this._map.getCenter();
665
- this._zoom = this._map.getZoom();
666
- },
667
-
668
- });
669
-
327
+ getEvents: function() {
328
+ return L.extend(markerProto.getEvents.apply(this, arguments), { rotate: this.update });
329
+ },
330
+ _initInteraction: function() {
331
+ var ret = markerProto._initInteraction.apply(this, arguments);
332
+ if (this.dragging && this.dragging.enabled() && this._map && this._map._rotate) {
333
+ markerDragProto = markerDragProto || Object.getPrototypeOf(this.dragging);
334
+ this.dragging.disable();
335
+ Object.assign(this.dragging, {
336
+ // _onDragStart: MarkerDrag._onDragStart.bind(this.dragging),
337
+ _onDrag: MarkerDrag._onDrag.bind(this.dragging),
338
+ _onDragEnd: MarkerDrag._onDragEnd.bind(this.dragging)
339
+ });
340
+ this.dragging.enable();
341
+ }
342
+ return ret;
343
+ },
344
+ _setPos: function(pos) {
345
+ if (this._map._rotate) {
346
+ pos = this._map.rotatedPointToMapPanePoint(pos);
347
+ }
348
+ var bearing = this.options.rotation || 0;
349
+ if (this.options.rotateWithView) {
350
+ bearing += this._map._bearing;
351
+ }
352
+ if (this.options.keepUpright) {
353
+ bearing = (bearing % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
354
+ if (bearing > Math.PI / 2 && bearing < 3 * Math.PI / 2) {
355
+ bearing += Math.PI;
356
+ }
357
+ bearing %= 2 * Math.PI;
358
+ }
359
+ if (this._icon) {
360
+ L.DomUtil.setPosition(this._icon, pos, bearing, pos, this.options.scale);
361
+ }
362
+ if (this._shadow) {
363
+ L.DomUtil.setPosition(this._shadow, pos, bearing, pos, this.options.scale);
364
+ }
365
+ this._zIndex = pos.y + this.options.zIndexOffset;
366
+ this._resetZIndex();
367
+ },
368
+ // _updateZIndex: function(offset) {
369
+ // if (!this._map._rotate) {
370
+ // return markerProto._updateZIndex.apply(this, arguments);
371
+ // }
372
+ // this._icon.style.zIndex = Math.round(this._zIndex + offset);
373
+ // },
374
+ setRotation: function(rotation) {
375
+ this.options.rotation = rotation;
376
+ this.update();
377
+ }
378
+ });
379
+
380
+ // src/layer/tile/GridLayer.js
381
+ var gridLayerProto = L.extend({}, L.GridLayer.prototype);
382
+ L.GridLayer.include({
670
383
  /**
671
- * @external L.Map
384
+ * Redraw L.TileLayer bounds after the map is
385
+ * moved by just calling `map.setBearing(theta)`
672
386
  *
673
- * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/Map.js
387
+ * @listens L.Map~rotate
674
388
  */
675
-
676
- const mapProto = L.extend({}, L.Map.prototype);
677
-
678
- L.Map.mergeOptions({ rotate: false, bearing: 0, });
679
-
680
- L.Map.include({
681
-
682
- /**
683
- * @param {(HTMLElement|String)} id html selector
684
- * @param {Object} [options={}] leaflet map options
685
- */
686
- initialize: function(id, options) {
687
- if (options.rotate) {
688
- this._rotate = true;
689
- this._bearing = 0;
690
- }
691
- mapProto.initialize.apply(this, arguments);
692
- if(this.options.rotate){
693
- this.setBearing(this.options.bearing);
694
- }
695
- },
696
-
697
- /**
698
- * Given a pixel coordinate relative to the map container,
699
- * returns the corresponding pixel coordinate relative to
700
- * the [origin pixel](#map-getpixelorigin).
701
- *
702
- * @param {L.Point} point pixel screen coordinates
703
- * @returns {L.Point} transformed pixel point
704
- */
705
- containerPointToLayerPoint: function(point) {
706
- if (!this._rotate) {
707
- return mapProto.containerPointToLayerPoint.apply(this, arguments);
708
- }
709
- return L.point(point)
710
- .subtract(this._getMapPanePos())
711
- .rotateFrom(-this._bearing, this._getRotatePanePos())
712
- .subtract(this._getRotatePanePos());
713
- },
714
-
715
- /**
716
- * Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
717
- * returns the corresponding pixel coordinate relative to the map container.
718
- *
719
- * @param {L.Point} point pixel screen coordinates
720
- * @returns {L.Point} transformed pixel point
721
- */
722
- layerPointToContainerPoint: function(point) {
723
- if (!this._rotate) {
724
- return mapProto.layerPointToContainerPoint.apply(this, arguments);
725
- }
726
- return L.point(point)
727
- .add(this._getRotatePanePos())
728
- .rotateFrom(this._bearing, this._getRotatePanePos())
729
- .add(this._getMapPanePos());
730
- },
731
-
732
- /**
733
- * Converts a coordinate from the rotated pane reference system
734
- * to the reference system of the not rotated map pane.
735
- *
736
- * (rotatePane) --> (mapPane)
737
- * (rotatePane) --> (norotatePane)
738
- *
739
- * @param {L.Point} point pixel screen coordinates
740
- * @returns {L.Point}
741
- *
742
- * @since leaflet-rotate (v0.1)
743
- */
744
- rotatedPointToMapPanePoint: function(point) {
745
- return L.point(point)
746
- .rotate(this._bearing)
747
- ._add(this._getRotatePanePos());
748
- },
749
-
750
- /**
751
- * Converts a coordinate from the not rotated map pane reference system
752
- * to the reference system of the rotated pane.
753
- *
754
- * (mapPane) --> (rotatePane)
755
- * (norotatePane) --> (rotatePane)
756
- *
757
- * @param {L.Point} point pixel screen coordinates
758
- *
759
- * @since leaflet-rotate (v0.1)
760
- */
761
- mapPanePointToRotatedPoint: function(point) {
762
- return L.point(point)
763
- ._subtract(this._getRotatePanePos())
764
- .rotate(-this._bearing);
765
- },
766
-
767
- // latLngToLayerPoint: function (latlng) {
768
- // var projectedPoint = this.project(L.latLng(latlng))._round();
769
- // return projectedPoint._subtract(this.getPixelOrigin());
770
- // },
771
-
772
- // latLngToContainerPoint: function (latlng) {
773
- // return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
774
- // },
775
-
776
- /**
777
- * Given latlng bounds, returns the bounds in projected pixel
778
- * relative to the map container.
779
- *
780
- * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/map/Map.js#L1114-L1135
781
- *
782
- * @param {L.LatLngBounds} bounds
783
- * @returns {L.Bounds}
784
- *
785
- * @since leaflet-rotate (v0.2)
786
- */
787
- mapBoundsToContainerBounds: function (bounds) {
788
- if (!this._rotate && mapProto.mapBoundsToContainerBounds) {
789
- return mapProto.mapBoundsToContainerBounds.apply(this, arguments);
790
- }
791
-
792
- // const nw = this.latLngToContainerPoint(bounds.getNorthWest()),
793
- // ne = this.latLngToContainerPoint(bounds.getNorthEast()),
794
- // sw = this.latLngToContainerPoint(bounds.getSouthWest()),
795
- // se = this.latLngToContainerPoint(bounds.getSouthEast());
796
-
797
- // same as `this.latLngToContainerPoint(latlng)` but with floating point precision
798
- const origin = this.getPixelOrigin();
799
- const nw = this.layerPointToContainerPoint(this.project(bounds.getNorthWest())._subtract(origin)),
800
- ne = this.layerPointToContainerPoint(this.project(bounds.getNorthEast())._subtract(origin)),
801
- sw = this.layerPointToContainerPoint(this.project(bounds.getSouthWest())._subtract(origin)),
802
- se = this.layerPointToContainerPoint(this.project(bounds.getSouthEast())._subtract(origin));
803
-
804
- return L.bounds([
805
- L.point(Math.min(nw.x, ne.x, se.x, sw.x), Math.min(nw.y, ne.y, se.y, sw.y)), // [ minX, minY ]
806
- L.point(Math.max(nw.x, ne.x, se.x, sw.x), Math.max(nw.y, ne.y, se.y, sw.y)) // [ maxX, maxY ]
807
- ]);
808
- },
809
-
810
- /**
811
- * Returns geographical bounds visible in the current map view
812
- *
813
- * @TODO find out if map bounds calculated by `L.Map::getBounds()`
814
- * function should match the `rotatePane` or `norotatePane` bounds
815
- *
816
- * @see https://github.com/fnicollet/Leaflet/issues/7
817
- *
818
- * @returns {L.LatLngBounds}
819
- */
820
- getBounds: function() {
821
- if (!this._rotate) {
822
- return mapProto.getBounds.apply(this, arguments);
823
- }
824
-
825
- // SEE: https://github.com/fnicollet/Leaflet/pull/22
826
- //
827
- // var bounds = this.getPixelBounds(),
828
- // sw = this.unproject(bounds.getBottomLeft()),
829
- // ne = this.unproject(bounds.getTopRight());
830
- // return new LatLngBounds(sw, ne);
831
- //
832
-
833
- // LatLngBounds' constructor automatically
834
- // extends the bounds to fit the passed points
835
- var size = this.getSize();
836
- return new L.LatLngBounds([
837
- this.containerPointToLatLng([0, 0]), // topleft
838
- this.containerPointToLatLng([size.x, 0]), // topright
839
- this.containerPointToLatLng([size.x, size.y]), // bottomright
840
- this.containerPointToLatLng([0, size.y]), // bottomleft
841
- ]);
842
- },
843
-
844
- /**
845
- * Returns the bounds of the current map view in projected pixel
846
- * coordinates (sometimes useful in layer and overlay implementations).
847
- *
848
- * @TODO find out if map bounds calculated by `L.Map::getPixelBounds()`
849
- * function should match the `rotatePane` or `norotatePane` bounds
850
- *
851
- * @see https://github.com/fnicollet/Leaflet/issues/7
852
- *
853
- * @returns {L.Bounds}
854
- */
855
- // getPixelBounds(center, zoom) {
856
- // // const topLeftPoint = map.containerPointToLayerPoint(this._getTopLeftPoint());
857
- // const topLeftPoint = this._getTopLeftPoint(center, zoom);
858
- // return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
859
- // },
860
-
861
- /**
862
- * Change map rotation
863
- *
864
- * @param {number} theta map degrees
865
- *
866
- * @since leaflet-rotate (v0.1)
867
- */
868
- setBearing: function(theta) {
869
- if (!L.Browser.any3d || !this._rotate) { return; }
870
-
871
- var bearing = L.Util.wrapNum(theta, [0, 360]) * L.DomUtil.DEG_TO_RAD,
872
- center = this._getPixelCenter(),
873
- oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center),
874
- newPos = oldPos.rotateFrom(bearing, center);
875
-
876
- // CSS transform
877
- L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center);
878
-
879
- this._pivot = center;
880
- this._bearing = bearing;
881
- this._rotatePanePos = newPos;
882
-
883
- this.fire('rotate');
884
- },
885
-
886
- /**
887
- * Get current map rotation
888
- *
889
- * @returns {number} theta map degrees
890
- *
891
- * @since leaflet-rotate (v0.1)
892
- */
893
- getBearing: function() {
894
- return this._bearing * L.DomUtil.RAD_TO_DEG;
895
- },
896
-
897
- /**
898
- * Creates a new [map pane](#map-pane) with the given name if it doesn't
899
- * exist already, then returns it. The pane is created as a child of
900
- * `container`, or as a child of the main map pane if not set.
901
- *
902
- * @param {String} name leaflet pane
903
- * @param {HTMLElement} [container] parent element
904
- * @returns {HTMLElement} pane container
905
- */
906
- // createPane: function(name, container) {
907
- // if (!this._rotate || name == 'mapPane') {
908
- // return mapProto.createPane.apply(this, arguments);
909
- // }
910
- // // init "rotatePane"
911
- // if (!this._rotatePane) {
912
- // // this._pivot = this.getSize().divideBy(2);
913
- // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane);
914
- // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot);
915
- // }
916
- // return mapProto.createPane.call(this, name, container || this._rotatePane);
917
- // },
918
-
919
- /**
920
- * Panes are DOM elements used to control the ordering of layers on
921
- * the map. You can access panes with [`map.getPane`](#map-getpane)
922
- * or [`map.getPanes`](#map-getpanes) methods. New panes can be created
923
- * with the [`map.createPane`](#map-createpane) method.
924
- *
925
- * Every map has the following default panes that differ only in zIndex:
926
- *
927
- * - mapPane [HTMLElement = 'auto'] - Pane that contains all other map panes
928
- * - tilePane [HTMLElement = 2] - Pane for tile layers
929
- * - overlayPane [HTMLElement = 4] - Pane for overlays like polylines and polygons
930
- * - shadowPane [HTMLElement = 5] - Pane for overlay shadows (e.g. marker shadows)
931
- * - markerPane [HTMLElement = 6] - Pane for marker icons
932
- * - tooltipPane [HTMLElement = 650] - Pane for tooltips.
933
- * - popupPane [HTMLElement = 700] - Pane for popups.
934
- */
935
- _initPanes: function() {
936
- var panes = this._panes = {};
937
- this._paneRenderers = {};
938
-
939
- this._mapPane = this.createPane('mapPane', this._container);
940
- L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
941
-
942
- if (this._rotate) {
943
- this._rotatePane = this.createPane('rotatePane', this._mapPane);
944
- this._norotatePane = this.createPane('norotatePane', this._mapPane);
945
- // rotatePane
946
- this.createPane('tilePane', this._rotatePane);
947
- this.createPane('overlayPane', this._rotatePane);
948
- // norotatePane
949
- this.createPane('shadowPane', this._norotatePane);
950
- this.createPane('markerPane', this._norotatePane);
951
- this.createPane('tooltipPane', this._norotatePane);
952
- this.createPane('popupPane', this._norotatePane);
953
- } else {
954
- this.createPane('tilePane');
955
- this.createPane('overlayPane');
956
- this.createPane('shadowPane');
957
- this.createPane('markerPane');
958
- this.createPane('tooltipPane');
959
- this.createPane('popupPane');
960
- }
961
-
962
- if (!this.options.markerZoomAnimation) {
963
- L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
964
- L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
965
- }
966
- },
967
-
968
- /**
969
- * Pans the map the minimum amount to make the `latlng` visible. Use
970
- * padding options to fit the display to more restricted bounds.
971
- * If `latlng` is already within the (optionally padded) display bounds,
972
- * the map will not be panned.
973
- *
974
- * @see https://github.com/Raruto/leaflet-rotate/issues/18
975
- *
976
- * @param {L.LatLng} latlng coordinates
977
- * @param {Object} [options={}] padding options
978
- *
979
- * @returns {L.Map} current map instance
980
- */
981
- panInside(latlng, options) {
982
- if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
983
- return mapProto.panInside.apply(this, arguments);
984
- }
985
-
986
- options = options || {};
987
-
988
- const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
989
- paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
990
- /** @TODO use mapProto.panInside */
991
- // pixelPoint = this.project(latlng),
992
- // pixelBounds = this.getPixelBounds(),
993
- // pixelCenter = this.project(this.getCenter()),
994
- rect = this._container.getBoundingClientRect(),
995
- pixelPoint = this.latLngToContainerPoint(latlng),
996
- pixelBounds = L.bounds([ L.point(rect), L.point(rect).add(this.getSize()) ]),
997
- pixelCenter = pixelBounds.getCenter(),
998
- //
999
- paddedBounds = L.bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
1000
- paddedSize = paddedBounds.getSize();
1001
-
1002
- if (!paddedBounds.contains(pixelPoint)) {
1003
- this._enforcingBounds = true;
1004
- const centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
1005
- const offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
1006
- pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
1007
- pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
1008
- /** @TODO use mapProto.panInside */
1009
- // this.panTo(this.unproject(pixelCenter), options);
1010
- this.panTo(this.containerPointToLatLng(pixelCenter), options);
1011
- //
1012
- this._enforcingBounds = false;
1013
- }
1014
- return this;
1015
- },
1016
-
1017
- /**
1018
- * Pans the map to the closest view that would lie inside the given bounds
1019
- * (if it's not already), controlling the animation using the options specific,
1020
- * if any.
1021
- *
1022
- * @TODO check if map bounds calculated by `L.Map::panInsideBounds()`
1023
- * function should match the `rotatePane` or `norotatePane` bounds
1024
- *
1025
- * @see https://github.com/fnicollet/Leaflet/issues/7
1026
- *
1027
- * @param {L.LatLngBounds} bounds coordinates
1028
- * @param {Object} [options] pan options
1029
- * @returns {L.Map} current map instance
1030
- */
1031
- // panInsideBounds: function (bounds, options) {
1032
- // this._enforcingBounds = true;
1033
- // var center = this.getCenter(),
1034
- // newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
1035
- //
1036
- // if (!center.equals(newCenter)) {
1037
- // this.panTo(newCenter, options);
1038
- // }
1039
- //
1040
- // this._enforcingBounds = false;
1041
- // return this;
1042
- // },
1043
-
1044
- // adjust center for view to get inside bounds
1045
- // _limitCenter(center, zoom, bounds) {
1046
- //
1047
- // if (!bounds) { return center; }
1048
- //
1049
- // const centerPoint = this.project(center, zoom),
1050
- // viewHalf = this.getSize().divideBy(2),
1051
- // viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
1052
- // offset = this._getBoundsOffset(viewBounds, bounds, zoom);
1053
- //
1054
- // // If offset is less than a pixel, ignore.
1055
- // // This prevents unstable projections from getting into
1056
- // // an infinite loop of tiny offsets.
1057
- // if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
1058
- // return center;
1059
- // }
1060
- //
1061
- // return this.unproject(centerPoint.add(offset), zoom);
1062
- // },
1063
-
1064
- // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
1065
- // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
1066
- // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
1067
- // flyToBounds(bounds, options) {
1068
- // const target = this._getBoundsCenterZoom(bounds, options);
1069
- // return this.flyTo(target.center, target.zoom, options);
1070
- // },
1071
-
1072
- // _getBoundsCenterZoom(bounds, options) {
1073
- //
1074
- // options = options || {};
1075
- // bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
1076
- //
1077
- // const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
1078
- // paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]);
1079
- //
1080
- // let zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
1081
- //
1082
- // zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
1083
- //
1084
- // if (zoom === Infinity) {
1085
- // return { center: bounds.getCenter(), zoom };
1086
- // }
1087
- //
1088
- // return { center, zoom };
1089
- //
1090
- // },
1091
-
1092
- /**
1093
- * Returns the maximum zoom level on which the given bounds fit to the map
1094
- * view in its entirety. If `inside` (optional) is set to `true`, the method
1095
- * instead returns the minimum zoom level on which the map view fits into
1096
- * the given bounds in its entirety.
1097
- *
1098
- * @param {L.LatLngBounds} bounds
1099
- * @param {Boolean} [inside=false]
1100
- * @param {L.Point} [padding=[0,0]]
1101
- *
1102
- * @returns {Number} zoom level
1103
- */
1104
- getBoundsZoom(bounds, inside, padding) {
1105
- if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
1106
- return mapProto.getBoundsZoom.apply(this, arguments);
1107
- }
1108
-
1109
- bounds = L.latLngBounds(bounds);
1110
- padding = L.point(padding || [0, 0]);
1111
-
1112
- let zoom = this.getZoom() || 0;
1113
- const min = this.getMinZoom(),
1114
- max = this.getMaxZoom(),
1115
- /** @TODO use mapProto.getBoundsZoom */
1116
- // nw = bounds.getNorthWest(),
1117
- // se = bounds.getSouthEast(),
1118
- // size = this.getSize().subtract(padding),
1119
- // boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
1120
- size = this.getSize().subtract(padding),
1121
- boundsSize = this.mapBoundsToContainerBounds(bounds).getSize(),
1122
- snap = this.options.zoomSnap,
1123
- scalex = size.x / boundsSize.x,
1124
- scaley = size.y / boundsSize.y,
1125
- scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
1126
-
1127
- zoom = this.getScaleZoom(scale, zoom);
1128
-
1129
- if (snap) {
1130
- zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
1131
- zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
1132
- }
1133
-
1134
- return Math.max(min, Math.min(max, zoom));
1135
- },
1136
-
1137
- /**
1138
- * Layer point of the current center
1139
- *
1140
- * @returns {L.Point} layer center
1141
- */
1142
- // _getCenterLayerPoint: function () {
1143
- // return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
1144
- // },
1145
-
1146
- /**
1147
- * Offset of the specified place to the current center in pixels
1148
- *
1149
- * @param {L.LatLng} latlng map coordinates
1150
- */
1151
- _getCenterOffset: function(latlng) {
1152
- var centerOffset = mapProto._getCenterOffset.apply(this, arguments);
1153
- if (this._rotate) {
1154
- centerOffset = centerOffset.rotate(this._bearing);
1155
- }
1156
- return centerOffset;
1157
- },
1158
-
1159
- /**
1160
- * @since leaflet-rotate (v0.1)
1161
- */
1162
- _getRotatePanePos: function() {
1163
- return this._rotatePanePos || new L.Point(0, 0);
1164
- // return L.DomUtil.getPosition(this._rotatePane) || new L.Point(0, 0);
1165
- },
1166
-
1167
- // _latLngToNewLayerPoint(latlng, zoom, center) {
1168
- // const topLeft = this._getNewPixelOrigin(center, zoom);
1169
- // return this.project(latlng, zoom)._subtract(topLeft);
1170
- //},
1171
-
1172
- _getNewPixelOrigin: function(center, zoom) {
1173
- if (!this._rotate) {
1174
- return mapProto._getNewPixelOrigin.apply(this, arguments);
1175
- }
1176
- var viewHalf = this.getSize()._divideBy(2);
1177
- return this.project(center, zoom)
1178
- .rotate(this._bearing)
1179
- ._subtract(viewHalf)
1180
- ._add(this._getMapPanePos())
1181
- ._add(this._getRotatePanePos())
1182
- .rotate(-this._bearing)
1183
- ._round();
1184
- },
1185
-
1186
- /**
1187
- * @since leaflet-rotate (v0.2)
1188
- *
1189
- * @see src\layer\tile\GridLayer::_getTiledPixelBounds()
1190
- */
1191
- _getNewPixelBounds: function(center, zoom) {
1192
- center = center || this.getCenter();
1193
- zoom = zoom || this.getZoom();
1194
- if (!this._rotate && mapProto._getNewPixelBounds) {
1195
- return mapProto._getNewPixelBounds.apply(this, arguments);
1196
- }
1197
- var mapZoom = this._animatingZoom ? Math.max(this._animateToZoom, this.getZoom()) : this.getZoom(),
1198
- scale = this.getZoomScale(mapZoom, zoom),
1199
- pixelCenter = this.project(center, zoom).floor(),
1200
- size = this.getSize(),
1201
- halfSize = new L.Bounds([
1202
- this.containerPointToLayerPoint([0, 0]).floor(),
1203
- this.containerPointToLayerPoint([size.x, 0]).floor(),
1204
- this.containerPointToLayerPoint([0, size.y]).floor(),
1205
- this.containerPointToLayerPoint([size.x, size.y]).floor()
1206
- ]).getSize().divideBy(scale * 2);
1207
-
1208
- return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
1209
- },
1210
-
1211
- /**
1212
- * @since leaflet-rotate (v0.2)
1213
- *
1214
- * @return {L.Point} map pivot point (center)
1215
- */
1216
- _getPixelCenter: function() {
1217
- if (!this._rotate && mapProto._getPixelCenter) {
1218
- return mapProto._getPixelCenter.apply(this, arguments);
1219
- }
1220
- return this.getSize()._divideBy(2)._subtract(this._getMapPanePos());
1221
- },
1222
-
1223
- /**
1224
- * @since leaflet-rotate (v0.2)
1225
- *
1226
- * @see src\layer\vector\Renderer::_update()
1227
- */
1228
- _getPaddedPixelBounds: function(padding) {
1229
- if (!this._rotate && mapProto._getPaddedPixelBounds) {
1230
- return mapProto._getPaddedPixelBounds.apply(this, arguments);
1231
- }
1232
- var p = padding,
1233
- size = this.getSize(),
1234
- padMin = size.multiplyBy(-p),
1235
- padMax = size.multiplyBy(1 + p);
1236
- //min = this.containerPointToLayerPoint(size.multiplyBy(-p)).round();
1237
-
1238
- return new L.Bounds([
1239
- this.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),
1240
- this.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),
1241
- this.containerPointToLayerPoint([padMax.x, padMin.y]).floor(),
1242
- this.containerPointToLayerPoint([padMax.x, padMax.y]).floor()
1243
- ]);
1244
- },
1245
-
1246
- _handleGeolocationResponse: function(pos) {
1247
- if (!this._container._leaflet_id) { return; }
1248
-
1249
- var lat = pos.coords.latitude,
1250
- lng = pos.coords.longitude,
1251
- /** @TODO use mapProto._handleGeolocationResponse */
1252
- hdg = pos.coords.heading,
1253
- latlng = new L.LatLng(lat, lng),
1254
- bounds = latlng.toBounds(pos.coords.accuracy),
1255
- options = this._locateOptions;
1256
-
1257
- if (options.setView) {
1258
- var zoom = this.getBoundsZoom(bounds);
1259
- this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
1260
- }
1261
-
1262
- var data = {
1263
- latlng: latlng,
1264
- bounds: bounds,
1265
- timestamp: pos.timestamp,
1266
- /** @TODO use mapProto._handleGeolocationResponse */
1267
- heading: hdg
1268
- };
1269
-
1270
- for (var i in pos.coords) {
1271
- if (typeof pos.coords[i] === 'number') {
1272
- data[i] = pos.coords[i];
1273
- }
1274
- }
1275
-
1276
- // @event locationfound: LocationEvent
1277
- // Fired when geolocation (using the [`locate`](#map-locate) method)
1278
- // went successfully.
1279
- this.fire('locationfound', data);
1280
- },
1281
-
1282
- /**
1283
- * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/geo/LatLngBounds.js#L253-L264
1284
- *
1285
- * @param {L.Bounds} points
1286
- * @returns {L.Bounds}
1287
- */
1288
- // toCircumscribedBounds(points) {
1289
- // var minX = points.reduce(function (pv, v) { return Math.min(pv, v.x); }, points[0].x),
1290
- // maxX = points.reduce(function (pv, v) { return Math.max(pv, v.x); }, points[0].x),
1291
- // minY = points.reduce(function (pv, v) { return Math.min(pv, v.y); }, points[0].y),
1292
- // maxY = points.reduce(function (pv, v) { return Math.max(pv, v.y); }, points[0].y);
1293
- //
1294
- // return L.bounds(L.point(minX, minY), L.point(maxX, maxY));
1295
- // },
1296
-
1297
- });
1298
-
389
+ getEvents: function() {
390
+ var events = gridLayerProto.getEvents.apply(this, arguments);
391
+ if (this._map._rotate && !this.options.updateWhenIdle) {
392
+ if (!this._onRotate) {
393
+ this._onRotate = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
394
+ }
395
+ events.rotate = this._onRotate;
396
+ }
397
+ return events;
398
+ },
399
+ _getTiledPixelBounds: function(center) {
400
+ if (!this._map._rotate) {
401
+ return gridLayerProto._getTiledPixelBounds.apply(this, arguments);
402
+ }
403
+ return this._map._getNewPixelBounds(center, this._tileZoom);
404
+ }
405
+ });
406
+
407
+ // src/layer/vector/Renderer.js
408
+ var rendererProto = L.extend({}, L.Renderer.prototype);
409
+ L.Renderer.include({
1299
410
  /**
1300
- * Rotates the map according to a smartphone's compass.
411
+ * Redraw L.Canvas and L.SVG renderer bounds after the
412
+ * map is moved by just calling `map.setBearing(theta)`
1301
413
  *
1302
- * @typedef L.Map.CompassBearing
414
+ * @listens L.Map~rotate
1303
415
  */
1304
-
1305
- L.Map.CompassBearing = L.Handler.extend({
1306
-
1307
- initialize: function(map) {
1308
- this._map = map;
1309
- /** @see https://caniuse.com/?search=DeviceOrientation */
1310
- if ('ondeviceorientationabsolute' in window) {
1311
- this.__deviceOrientationEvent = 'deviceorientationabsolute';
1312
- } else if('ondeviceorientation' in window) {
1313
- this.__deviceOrientationEvent = 'deviceorientation';
1314
- }
1315
- this._throttled = L.Util.throttle(this._onDeviceOrientation, 100, this);
1316
- },
1317
-
1318
- addHooks: function() {
1319
- if (this._map._rotate && this.__deviceOrientationEvent) {
1320
- L.DomEvent.on(window, this.__deviceOrientationEvent, this._throttled, this);
1321
- } else {
1322
- // L.Map.CompassBearing handler will be automatically
1323
- // disabled if device orientation is not supported.
1324
- this.disable();
1325
- }
1326
- },
1327
-
1328
- removeHooks: function() {
1329
- if (this._map._rotate && this.__deviceOrientationEvent) {
1330
- L.DomEvent.off(window, this.__deviceOrientationEvent, this._throttled, this);
1331
- }
1332
- },
1333
-
1334
- /**
1335
- * `DeviceOrientationEvent.absolute` - Indicates whether the device is providing absolute
1336
- * orientation values (relatives to Magnetic North) or
1337
- * using some arbitrary frame determined by the device.
1338
- *
1339
- * `DeviceOrientationEvent.alpha` - Returns the rotation of the device around the Z axis;
1340
- * that is, the number of degrees by which the device is
1341
- * being twisted around the center of the screen.
1342
- *
1343
- * `window.orientation` - Returns the screen orientation in degrees (in 90-degree increments)
1344
- * of the viewport relative to the device's natural orientation.
1345
- * Its only possible values are -90, 0, 90, and 180. Positive
1346
- * values are counterclockwise; negative values are clockwise.
1347
- *
1348
- * @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/absolute
1349
- * @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/alpha
1350
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation
1351
- */
1352
- _onDeviceOrientation: function(e) {
1353
- var angle = e.webkitCompassHeading || e.alpha;
1354
- var deviceOrientation = 0;
1355
-
1356
- // Safari iOS
1357
- if (!e.absolute && e.webkitCompassHeading) {
1358
- angle = 360 - angle;
1359
- }
1360
-
1361
- // Older browsers
1362
- if (!e.absolute && 'undefined' !== typeof window.orientation) {
1363
- deviceOrientation = window.orientation;
1364
- }
1365
-
1366
- this._map.setBearing(angle - deviceOrientation);
1367
- },
1368
-
1369
- });
1370
-
416
+ getEvents: function() {
417
+ return L.extend(rendererProto.getEvents.apply(this, arguments), { rotate: this._update });
418
+ },
1371
419
  /**
1372
- * Add Compass bearing handler to L.Map (disabled unless `window.DeviceOrientationEvent` is set).
420
+ * Fix for `map.flyTo()` when `false === map.options.zoomAnimation`
1373
421
  *
1374
- * @property {L.Map.CompassBearing} compassBearing
422
+ * @see https://github.com/Leaflet/Leaflet/pull/8794
1375
423
  */
1376
- L.Map.addInitHook('addHandler', 'compassBearing', L.Map.CompassBearing);
1377
-
424
+ onAdd: function() {
425
+ rendererProto.onAdd.apply(this, arguments);
426
+ if (L.version <= "1.9.3") {
427
+ this._container.classList.add("leaflet-zoom-animated");
428
+ }
429
+ },
1378
430
  /**
1379
- * Triggers `invalidateResize` when the map's DOM container mutates.
431
+ * @FIXME layer drifts on `map.setZoom()` (eg. zoom during animation)
432
+ *
433
+ * the main cause seems to be related to `this._updateTransform(path._center, path._zoom))`
434
+ * and `this._topLeft = this._map.layerPointToLatLng(this._bounds.min);`
1380
435
  *
1381
- * @typedef L.Map.ContainerMutation
436
+ * @example
437
+ * map.setZoom(2);
438
+ * path._renderer._update();
439
+ * path._renderer._updateTransform(path._renderer._center, path._renderer._zoom);
440
+ *
441
+ * @see https://github.com/Leaflet/Leaflet/pull/8794
442
+ * @see https://github.com/Leaflet/Leaflet/pull/8103
443
+ * @see https://github.com/Leaflet/Leaflet/issues/7466
444
+ *
445
+ * @TODO rechek this changes from leaflet@v1.9.3
446
+ *
447
+ * @see https://github.com/Leaflet/Leaflet/compare/v1.7.0...v1.9.3
1382
448
  */
1383
-
449
+ _updateTransform: function(center, zoom) {
450
+ if (!this._map._rotate) {
451
+ return rendererProto._updateTransform.apply(this, arguments);
452
+ }
453
+ if (zoom === this._zoom) {
454
+ return;
455
+ }
456
+ var scale = this._map.getZoomScale(zoom, this._zoom), offset = this._map._latLngToNewLayerPoint(this._topLeft, zoom, center);
457
+ L.DomUtil.setTransform(this._container, offset, scale);
458
+ },
459
+ // getEvents() {
460
+ // const events = {
461
+ // viewreset: this._reset,
462
+ // zoom: this._onZoom,
463
+ // moveend: this._update,
464
+ // zoomend: this._onZoomEnd
465
+ // };
466
+ // if (this._zoomAnimated) {
467
+ // events.zoomanim = this._onAnimZoom;
468
+ // }
469
+ // return events;
470
+ // },
471
+ // _onAnimZoom(ev) {
472
+ // this._updateTransform(ev.center, ev.zoom);
473
+ // },
474
+ // _onZoom() {
475
+ // this._updateTransform(this._map.getCenter(), this._map.getZoom());
476
+ // },
477
+ // _onZoomEnd() {
478
+ // for (const id in this._layers) {
479
+ // this._layers[id]._project();
480
+ // }
481
+ // },
482
+ // _reset() {
483
+ // this._update();
484
+ // this._updateTransform(this._center, this._zoom);
485
+ // for (const id in this._layers) {
486
+ // this._layers[id]._reset();
487
+ // }
488
+ // },
489
+ // _updatePaths() {
490
+ // for (const id in this._layers) {
491
+ // this._layers[id]._update();
492
+ // }
493
+ // },
494
+ _update: function() {
495
+ if (!this._map._rotate) {
496
+ return rendererProto._update.apply(this, arguments);
497
+ }
498
+ this._bounds = this._map._getPaddedPixelBounds(this.options.padding);
499
+ this._topLeft = this._map.layerPointToLatLng(this._bounds.min);
500
+ this._center = this._map.getCenter();
501
+ this._zoom = this._map.getZoom();
502
+ }
503
+ });
504
+
505
+ // src/map/Map.js
506
+ var mapProto = L.extend({}, L.Map.prototype);
507
+ L.Map.mergeOptions({ rotate: false, bearing: 0 });
508
+ L.Map.include({
509
+ _animateZoomNoDelay: function(center, zoom) {
510
+ if (!this._mapPane) {
511
+ return;
512
+ }
513
+ this._animatingZoom = true;
514
+ this._animateToCenter = center;
515
+ this._animateToZoom = zoom;
516
+ this._move(center, zoom, void 0, true);
517
+ this._onZoomTransitionEnd();
518
+ },
1384
519
  /**
1385
- * @TODO check again this file after leaflet v1.9.3 (eg. L.Browser.mutation).
1386
- * Mutation Observer support will likely be added by default in next releases.
520
+ * @param {(HTMLElement|String)} id html selector
521
+ * @param {Object} [options={}] leaflet map options
1387
522
  */
1388
-
1389
- L.Map.mergeOptions({
1390
-
1391
- /**
1392
- * Whether the map uses mutation observers to
1393
- * detect changes in its container and trigger
1394
- * `invalidateSize`. Disabled by default due to
1395
- * support not being available in all web browsers.
1396
- *
1397
- * @type {Boolean}
1398
- *
1399
- * @see https://developer.mozilla.org/docs/Web/API/MutationObserver
1400
- */
1401
- trackContainerMutation: false
1402
-
1403
- });
1404
-
1405
- L.Map.ContainerMutation = L.Handler.extend({
1406
-
1407
- addHooks: function() {
1408
- // if (!L.Browser.mutation) { return; }
1409
- if (!this._observer) {
1410
- this._observer = new MutationObserver(L.Util.bind(this._map.invalidateSize, this._map));
1411
- }
1412
- this._observer.observe(this._map.getContainer(), {
1413
- childList: false,
1414
- attributes: true,
1415
- characterData: false,
1416
- subtree: false,
1417
- attributeFilter: ['style']
1418
- });
1419
- },
1420
-
1421
- removeHooks: function() {
1422
- // if (!L.Browser.mutation) { return; }
1423
- this._observer.disconnect();
1424
- },
1425
-
1426
- });
1427
-
523
+ initialize: function(id, options) {
524
+ if (options.rotate) {
525
+ this._rotate = true;
526
+ this._bearing = 0;
527
+ }
528
+ mapProto.initialize.apply(this, arguments);
529
+ if (this.options.rotate) {
530
+ this.setBearing(this.options.bearing);
531
+ }
532
+ },
1428
533
  /**
1429
- * Add Container mutation handler to L.Map (disabled unless `trackContainerMutation` is set).
534
+ * Given a pixel coordinate relative to the map container,
535
+ * returns the corresponding pixel coordinate relative to
536
+ * the [origin pixel](#map-getpixelorigin).
1430
537
  *
1431
- * @property {L.Map.ContainerMutation} trackContainerMutation
538
+ * @param {L.Point} point pixel screen coordinates
539
+ * @returns {L.Point} transformed pixel point
1432
540
  */
1433
- L.Map.addInitHook('addHandler', 'trackContainerMutation', L.Map.ContainerMutation);
1434
-
541
+ containerPointToLayerPoint: function(point) {
542
+ if (!this._rotate) {
543
+ return mapProto.containerPointToLayerPoint.apply(this, arguments);
544
+ }
545
+ return L.point(point).subtract(this._getMapPanePos()).rotateFrom(-this._bearing, this._getRotatePanePos()).subtract(this._getRotatePanePos());
546
+ },
547
+ /**
548
+ * Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
549
+ * returns the corresponding pixel coordinate relative to the map container.
550
+ *
551
+ * @param {L.Point} point pixel screen coordinates
552
+ * @returns {L.Point} transformed pixel point
553
+ */
554
+ layerPointToContainerPoint: function(point) {
555
+ if (!this._rotate) {
556
+ return mapProto.layerPointToContainerPoint.apply(this, arguments);
557
+ }
558
+ return L.point(point).add(this._getRotatePanePos()).rotateFrom(this._bearing, this._getRotatePanePos()).add(this._getMapPanePos());
559
+ },
1435
560
  /**
1436
- * TouchGestures is both TouchZoom plus TouchRotate
561
+ * Converts a coordinate from the rotated pane reference system
562
+ * to the reference system of the not rotated map pane.
563
+ *
564
+ * (rotatePane) --> (mapPane)
565
+ * (rotatePane) --> (norotatePane)
1437
566
  *
1438
- * @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834
1439
- * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
567
+ * @param {L.Point} point pixel screen coordinates
568
+ * @returns {L.Point}
1440
569
  *
1441
- * @typedef L.Map.TouchGestures
570
+ * @since leaflet-rotate (v0.1)
1442
571
  */
1443
-
1444
- L.Map.mergeOptions({
1445
-
1446
- /**
1447
- * Set it to false if you don't want the map to
1448
- * zoom beyond min/max zoom and then bounce back
1449
- * when pinch-zooming.
1450
- *
1451
- * @type {Boolean}
1452
- */
1453
- bounceAtZoomLimits: true,
1454
-
1455
- });
1456
-
1457
- L.Map.TouchGestures = L.Handler.extend({
1458
-
1459
- initialize: function(map) {
1460
- this._map = map;
1461
- this.rotate = !!this._map.options.touchRotate;
1462
- this.zoom = !!this._map.options.touchZoom;
1463
- },
1464
-
1465
- addHooks: function() {
1466
- L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
1467
- },
1468
-
1469
- removeHooks: function() {
1470
- L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
1471
- },
1472
-
1473
- _onTouchStart: function(e) {
1474
- var map = this._map;
1475
-
1476
- if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming || this._rotating) { return; }
1477
-
1478
- var p1 = map.mouseEventToContainerPoint(e.touches[0]),
1479
- p2 = map.mouseEventToContainerPoint(e.touches[1]),
1480
- vector = p1.subtract(p2);
1481
-
1482
- this._centerPoint = map.getSize()._divideBy(2);
1483
- this._startLatLng = map.containerPointToLatLng(this._centerPoint);
1484
-
1485
- if (this.zoom) {
1486
- if (map.options.touchZoom !== 'center') {
1487
- this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
1488
- }
1489
- this._startDist = p1.distanceTo(p2);
1490
- this._startZoom = map.getZoom();
1491
- this._zooming = true;
1492
- } else {
1493
- this._zooming = false;
1494
- }
1495
-
1496
- if (this.rotate) {
1497
- this._startTheta = Math.atan(vector.x / vector.y);
1498
- this._startBearing = map.getBearing();
1499
- if (vector.y < 0) { this._startBearing += 180; }
1500
- this._rotating = true;
1501
- } else {
1502
- this._rotating = false;
1503
- }
1504
-
1505
- this._moved = false;
1506
-
1507
- map._stop();
1508
-
1509
- L.DomEvent
1510
- .on(document, 'touchmove', this._onTouchMove, this)
1511
- .on(document, 'touchend touchcancel', this._onTouchEnd, this);
1512
-
1513
- L.DomEvent.preventDefault(e);
1514
- },
1515
-
1516
- _onTouchMove: function(e) {
1517
- if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) { return; }
1518
-
1519
- var map = this._map,
1520
- p1 = map.mouseEventToContainerPoint(e.touches[0]),
1521
- p2 = map.mouseEventToContainerPoint(e.touches[1]),
1522
- vector = p1.subtract(p2),
1523
- scale = p1.distanceTo(p2) / this._startDist,
1524
- delta;
1525
-
1526
- if (this._rotating) {
1527
- var theta = Math.atan(vector.x / vector.y);
1528
- var bearingDelta = (theta - this._startTheta) * L.DomUtil.RAD_TO_DEG;
1529
- if (vector.y < 0) { bearingDelta += 180; }
1530
- if (bearingDelta) {
1531
- /**
1532
- * @TODO the pivot should be the last touch point,
1533
- * but zoomAnimation manages to overwrite the rotate
1534
- * pane position. Maybe related to #3529.
1535
- *
1536
- * @see https://github.com/Leaflet/Leaflet/pull/3529
1537
- * @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834
1538
- */
1539
- map.setBearing(this._startBearing - bearingDelta);
1540
- }
1541
- }
1542
-
1543
- if (this._zooming) {
1544
- this._zoom = map.getScaleZoom(scale, this._startZoom);
1545
-
1546
- if (!map.options.bounceAtZoomLimits && (
1547
- (this._zoom < map.getMinZoom() && scale < 1) ||
1548
- (this._zoom > map.getMaxZoom() && scale > 1))) {
1549
- this._zoom = map._limitZoom(this._zoom);
1550
- }
1551
-
1552
- if (map.options.touchZoom === 'center') {
1553
- this._center = this._startLatLng;
1554
- if (scale === 1) { return; }
1555
- } else {
1556
- // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
1557
- delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
1558
- if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
1559
-
1560
- var alpha = -map.getBearing() * L.DomUtil.DEG_TO_RAD;
1561
-
1562
- this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(delta.rotate(alpha)));
1563
- }
1564
-
1565
- }
1566
-
1567
- if (!this._moved) {
1568
- map._moveStart(true, false);
1569
- this._moved = true;
1570
- }
1571
-
1572
- L.Util.cancelAnimFrame(this._animRequest);
1573
-
1574
- var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined);
1575
- this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
1576
-
1577
- L.DomEvent.preventDefault(e);
1578
- },
1579
-
1580
- _onTouchEnd: function() {
1581
- if (!this._moved || !(this._zooming || this._rotating)) {
1582
- this._zooming = false;
1583
- return;
1584
- }
1585
-
1586
- this._zooming = false;
1587
- this._rotating = false;
1588
- L.Util.cancelAnimFrame(this._animRequest);
1589
-
1590
- L.DomEvent
1591
- .off(document, 'touchmove', this._onTouchMove, this)
1592
- .off(document, 'touchend touchcancel', this._onTouchEnd, this);
1593
-
1594
- if (this.zoom) {
1595
- // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
1596
- if (this._map.options.zoomAnimation) {
1597
- this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
1598
- } else {
1599
- this._map._resetView(this._center, this._map._limitZoom(this._zoom));
1600
- }
1601
- }
1602
- },
1603
-
1604
- });
1605
-
572
+ rotatedPointToMapPanePoint: function(point) {
573
+ return L.point(point).rotate(this._bearing)._add(this._getRotatePanePos());
574
+ },
1606
575
  /**
1607
- * Add Touch Gestures handler (enabled unless `touchGestures` is unset).
576
+ * Converts a coordinate from the not rotated map pane reference system
577
+ * to the reference system of the rotated pane.
1608
578
  *
1609
- * @property {L.Map.TouchGestures} touchGestures
579
+ * (mapPane) --> (rotatePane)
580
+ * (norotatePane) --> (rotatePane)
581
+ *
582
+ * @param {L.Point} point pixel screen coordinates
583
+ *
584
+ * @since leaflet-rotate (v0.1)
1610
585
  */
1611
- L.Map.addInitHook('addHandler', 'touchGestures', L.Map.TouchGestures);
1612
-
586
+ mapPanePointToRotatedPoint: function(point) {
587
+ return L.point(point)._subtract(this._getRotatePanePos()).rotate(-this._bearing);
588
+ },
589
+ // latLngToLayerPoint: function (latlng) {
590
+ // var projectedPoint = this.project(L.latLng(latlng))._round();
591
+ // return projectedPoint._subtract(this.getPixelOrigin());
592
+ // },
593
+ // latLngToContainerPoint: function (latlng) {
594
+ // return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
595
+ // },
1613
596
  /**
1614
- * Rotates the map on two-finger (touch devices).
597
+ * Given latlng bounds, returns the bounds in projected pixel
598
+ * relative to the map container.
1615
599
  *
1616
- * @typedef L.Map.TouchRotate
600
+ * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/map/Map.js#L1114-L1135
601
+ *
602
+ * @param {L.LatLngBounds} bounds
603
+ * @returns {L.Bounds}
604
+ *
605
+ * @since leaflet-rotate (v0.2)
1617
606
  */
1618
-
1619
- L.Map.mergeOptions({
1620
-
1621
- /**
1622
- * Whether the map can be rotated with a two-finger rotation gesture
1623
- *
1624
- * @type {Boolean}
1625
- */
1626
- touchRotate: false,
1627
-
1628
- });
1629
-
1630
- L.Map.TouchRotate = L.Handler.extend({
1631
-
1632
- addHooks: function() {
1633
- this._map.touchGestures.enable();
1634
- this._map.touchGestures.rotate = true;
1635
- },
1636
-
1637
- removeHooks: function() {
1638
- this._map.touchGestures.rotate = false;
1639
- },
1640
-
1641
- });
1642
-
607
+ mapBoundsToContainerBounds: function(bounds) {
608
+ if (!this._rotate && mapProto.mapBoundsToContainerBounds) {
609
+ return mapProto.mapBoundsToContainerBounds.apply(this, arguments);
610
+ }
611
+ const origin = this.getPixelOrigin();
612
+ 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.bounds([
614
+ L.point(Math.min(nw.x, ne.x, se.x, sw.x), Math.min(nw.y, ne.y, se.y, sw.y)),
615
+ // [ minX, minY ]
616
+ L.point(Math.max(nw.x, ne.x, se.x, sw.x), Math.max(nw.y, ne.y, se.y, sw.y))
617
+ // [ maxX, maxY ]
618
+ ]);
619
+ },
1643
620
  /**
1644
- * Add Touch Rotate handler (disabled unless `touchGestures` is set).
621
+ * Returns geographical bounds visible in the current map view
622
+ *
623
+ * @TODO find out if map bounds calculated by `L.Map::getBounds()`
624
+ * function should match the `rotatePane` or `norotatePane` bounds
625
+ *
626
+ * @see https://github.com/fnicollet/Leaflet/issues/7
1645
627
  *
1646
- * @property {L.Map.TouchGestures} touchGestures
628
+ * @returns {L.LatLngBounds}
1647
629
  */
1648
- L.Map.addInitHook('addHandler', 'touchRotate', L.Map.TouchRotate);
1649
-
630
+ getBounds: function() {
631
+ if (!this._rotate) {
632
+ return mapProto.getBounds.apply(this, arguments);
633
+ }
634
+ var size = this.getSize();
635
+ return new L.LatLngBounds([
636
+ this.containerPointToLatLng([0, 0]),
637
+ // topleft
638
+ this.containerPointToLatLng([size.x, 0]),
639
+ // topright
640
+ this.containerPointToLatLng([size.x, size.y]),
641
+ // bottomright
642
+ this.containerPointToLatLng([0, size.y])
643
+ // bottomleft
644
+ ]);
645
+ },
1650
646
  /**
1651
- * Rotates the map on shift key + mousewheel scrolling (desktop).
647
+ * Returns the bounds of the current map view in projected pixel
648
+ * coordinates (sometimes useful in layer and overlay implementations).
649
+ *
650
+ * @TODO find out if map bounds calculated by `L.Map::getPixelBounds()`
651
+ * function should match the `rotatePane` or `norotatePane` bounds
652
+ *
653
+ * @see https://github.com/fnicollet/Leaflet/issues/7
1652
654
  *
1653
- * @typedef L.Map.ShiftKeyRotate
655
+ * @returns {L.Bounds}
1654
656
  */
1655
-
1656
- L.Map.mergeOptions({
1657
-
1658
- /**
1659
- * Whether the map can be rotated with shift + wheel scroll
1660
- * @type {Boolean}
1661
- */
1662
- shiftKeyRotate: true,
1663
-
1664
- });
1665
-
1666
- L.Map.ShiftKeyRotate = L.Handler.extend({
1667
-
1668
- addHooks: function() {
1669
- L.DomEvent.on(this._map._container, "wheel", this._handleShiftScroll, this);
1670
- // this._map.shiftKeyRotate.enable();
1671
- this._map.shiftKeyRotate.rotate = true;
1672
- },
1673
-
1674
- removeHooks: function() {
1675
- L.DomEvent.off(this._map._container, "wheel", this._handleShiftScroll, this);
1676
- this._map.shiftKeyRotate.rotate = false;
1677
- },
1678
-
1679
- _handleShiftScroll: function(e) {
1680
- if (e.shiftKey) {
1681
- e.preventDefault();
1682
- this._map.scrollWheelZoom.disable();
1683
- this._map.setBearing((this._map._bearing * L.DomUtil.RAD_TO_DEG) + Math.sign(e.deltaY) * 5);
1684
- } else {
1685
- this._map.scrollWheelZoom.enable();
1686
- }
1687
- },
1688
-
1689
- });
1690
-
657
+ // getPixelBounds(center, zoom) {
658
+ // // const topLeftPoint = map.containerPointToLayerPoint(this._getTopLeftPoint());
659
+ // const topLeftPoint = this._getTopLeftPoint(center, zoom);
660
+ // return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
661
+ // },
1691
662
  /**
1692
- * Add ShiftKey handler to L.Map (enabled unless `shiftKeyRotate` is unset).
663
+ * Change map rotation
664
+ *
665
+ * @param {number} theta map degrees
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]
1693
670
  *
1694
- * @property {L.Map.ShiftKeyRotate} shiftKeyRotate
671
+ * @since leaflet-rotate (v0.1)
1695
672
  */
1696
- L.Map.addInitHook('addHandler', 'shiftKeyRotate', L.Map.ShiftKeyRotate);
1697
-
1698
- // decrease `scrollWheelZoom` handler priority over `shiftKeyRotate` handler
1699
- L.Map.addInitHook(function() {
1700
- if (this.scrollWheelZoom.enabled() && this.shiftKeyRotate.enabled()) {
1701
- this.scrollWheelZoom.disable();
1702
- this.scrollWheelZoom.enable();
673
+ setBearing: function(theta, options) {
674
+ if (!L.Browser.any3d || !this._rotate) {
675
+ return;
676
+ }
677
+ if (options && options.animate) {
678
+ this._bearingAnimationId = (this._bearingAnimationId || 0) + 1;
679
+ var animationId = this._bearingAnimationId;
680
+ if (this._bearingAnimationFrame) {
681
+ L.Util.cancelAnimFrame(this._bearingAnimationFrame);
1703
682
  }
1704
- });
1705
-
683
+ var 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();
684
+ var animate = function() {
685
+ if (!this._mapPane) {
686
+ delete this._bearingAnimationFrame;
687
+ return;
688
+ }
689
+ var progress = duration ? Math.min((performance.now() - startTime) / duration, 1) : 1, eased = 1 - Math.pow(1 - progress, 1 / easeLinearity);
690
+ this._setBearing(progress === 1 ? endBearing : startBearing + delta * eased);
691
+ if (this._bearingAnimationId !== animationId) {
692
+ return;
693
+ }
694
+ if (progress < 1) {
695
+ this._bearingAnimationFrame = L.Util.requestAnimFrame(animate, this);
696
+ } else {
697
+ delete this._bearingAnimationFrame;
698
+ }
699
+ };
700
+ this._bearingAnimationFrame = L.Util.requestAnimFrame(animate, this);
701
+ return;
702
+ }
703
+ this._bearingAnimationId = (this._bearingAnimationId || 0) + 1;
704
+ if (this._bearingAnimationFrame) {
705
+ L.Util.cancelAnimFrame(this._bearingAnimationFrame);
706
+ delete this._bearingAnimationFrame;
707
+ }
708
+ this._setBearing(theta);
709
+ },
710
+ _setBearing: function(theta) {
711
+ var bearing = L.Util.wrapNum(theta, [0, 360]) * L.DomUtil.DEG_TO_RAD, center = this._getPixelCenter(), oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center), newPos = oldPos.rotateFrom(bearing, center);
712
+ L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center);
713
+ this._pivot = center;
714
+ this._bearing = bearing;
715
+ this._rotatePanePos = newPos;
716
+ this.fire("rotate");
717
+ },
1706
718
  /**
1707
- * Adds pinch zoom rotation on mobile browsers
719
+ * Get current map rotation
1708
720
  *
1709
- * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
721
+ * @returns {number} theta map degrees
1710
722
  *
1711
- * @external L.Map.TouchZoom
723
+ * @since leaflet-rotate (v0.1)
1712
724
  */
1713
-
1714
- L.Map.mergeOptions({
1715
-
1716
- /**
1717
- * Whether the map can be zoomed by touch-dragging
1718
- * with two fingers. If passed `'center'`, it will
1719
- * zoom to the center of the view regardless of
1720
- * where the touch events (fingers) were. Enabled
1721
- * for touch-capable web browsers.
1722
- *
1723
- * @type {(Boolean|String)}
1724
- */
1725
- touchZoom: L.Browser.touch,
1726
-
1727
- /**
1728
- * @TODO check if this is a duplicate of `L.Map.TouchGestures::bounceAtZoomLimits`
1729
- *
1730
- * Set it to false if you don't want the map to
1731
- * zoom beyond min/max zoom and then bounce back
1732
- * when pinch-zooming.
1733
- *
1734
- * @type {Boolean}
1735
- */
1736
- bounceAtZoomLimits: false,
1737
-
1738
- });
1739
-
1740
- L.Map.TouchZoom = L.Handler.extend({
1741
-
1742
- addHooks: function() {
1743
- L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
1744
- this._map.touchGestures.enable();
1745
- this._map.touchGestures.zoom = true;
1746
- },
1747
-
1748
- removeHooks: function() {
1749
- L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
1750
- this._map.touchGestures.zoom = false;
1751
- },
1752
-
1753
- });
1754
-
725
+ getBearing: function() {
726
+ return this._bearing * L.DomUtil.RAD_TO_DEG;
727
+ },
1755
728
  /**
1756
- * Add Touch Zoom handler (disabled unless `L.Browser.touch` is set).
729
+ * Creates a new [map pane](#map-pane) with the given name if it doesn't
730
+ * exist already, then returns it. The pane is created as a child of
731
+ * `container`, or as a child of the main map pane if not set.
1757
732
  *
1758
- * @property {L.Map.TouchGestures} touchGestures
733
+ * @param {String} name leaflet pane
734
+ * @param {HTMLElement} [container] parent element
735
+ * @returns {HTMLElement} pane container
1759
736
  */
1760
- L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
1761
-
737
+ // createPane: function(name, container) {
738
+ // if (!this._rotate || name == 'mapPane') {
739
+ // return mapProto.createPane.apply(this, arguments);
740
+ // }
741
+ // // init "rotatePane"
742
+ // if (!this._rotatePane) {
743
+ // // this._pivot = this.getSize().divideBy(2);
744
+ // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane);
745
+ // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot);
746
+ // }
747
+ // return mapProto.createPane.call(this, name, container || this._rotatePane);
748
+ // },
1762
749
  /**
1763
- * A tri-state control for map rotation, states are:
750
+ * Panes are DOM elements used to control the ordering of layers on
751
+ * the map. You can access panes with [`map.getPane`](#map-getpane)
752
+ * or [`map.getPanes`](#map-getpanes) methods. New panes can be created
753
+ * with the [`map.createPane`](#map-createpane) method.
1764
754
  *
1765
- * - Locked (default)
1766
- * - Unlocked (user can pinch-rotate)
1767
- * - Follow (rotation follows device orientation, if available)
755
+ * Every map has the following default panes that differ only in zIndex:
1768
756
  *
1769
- * @typedef L.Control.Rotate
757
+ * - mapPane [HTMLElement = 'auto'] - Pane that contains all other map panes
758
+ * - tilePane [HTMLElement = 2] - Pane for tile layers
759
+ * - overlayPane [HTMLElement = 4] - Pane for overlays like polylines and polygons
760
+ * - shadowPane [HTMLElement = 5] - Pane for overlay shadows (e.g. marker shadows)
761
+ * - markerPane [HTMLElement = 6] - Pane for marker icons
762
+ * - tooltipPane [HTMLElement = 650] - Pane for tooltips.
763
+ * - popupPane [HTMLElement = 700] - Pane for popups.
1770
764
  */
1771
-
1772
- L.Control.Rotate = L.Control.extend({
1773
-
1774
- options: {
1775
- position: 'topleft',
1776
- closeOnZeroBearing: true
1777
- },
1778
-
1779
- onAdd: function(map) {
1780
- var container = this._container = L.DomUtil.create('div', 'leaflet-control-rotate leaflet-bar');
1781
-
1782
- // this.button = L.Control.Zoom.prototype._createButton.call(this, 'R', 'leaflet-control-rotate', 'leaflet-control-rotate', container, this._toggleLock);
1783
-
1784
- var arrow = this._arrow = L.DomUtil.create('span', 'leaflet-control-rotate-arrow');
1785
-
1786
- 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")`;
1787
- arrow.style.cursor = 'grab';
1788
- arrow.style.display = 'block';
1789
- arrow.style.width = '100%';
1790
- arrow.style.height = '100%';
1791
- arrow.style.backgroundRepeat = 'no-repeat';
1792
- arrow.style.backgroundPosition = '50%';
1793
-
1794
- // Copy-pasted from L.Control.Zoom
1795
- var link = this._link = L.DomUtil.create('a', 'leaflet-control-rotate-toggle', container);
1796
- link.appendChild(arrow);
1797
- link.href = '#';
1798
- link.title = 'Rotate map';
1799
-
1800
- L.DomEvent
1801
- .on(link, 'dblclick', L.DomEvent.stopPropagation)
1802
- .on(link, 'mousedown', this._handleMouseDown, this)
1803
- .on(link, 'click', L.DomEvent.stop)
1804
- .on(link, 'click', this._cycleState, this)
1805
- .on(link, 'click', this._refocusOnMap, this);
1806
-
1807
- if (!L.Browser.any3d) {
1808
- L.DomUtil.addClass(link, 'leaflet-disabled');
1809
- }
1810
-
1811
- this._restyle();
1812
-
1813
- map.on('rotate', this._restyle, this);
1814
-
1815
- // State flag
1816
- this._follow = false;
1817
- this._canFollow = false;
1818
-
1819
- if (this.options.closeOnZeroBearing && map.getBearing() === 0) {
1820
- container.style.display = 'none';
1821
- }
1822
-
1823
- return container;
1824
- },
1825
-
1826
- onRemove: function(map) {
1827
- map.off('rotate', this._restyle, this);
1828
- },
1829
-
1830
- _handleMouseDown: function(e) {
1831
- L.DomEvent.stop(e);
1832
- this.dragging = true;
1833
- this.dragstartX = e.pageX;
1834
- this.dragstartY = e.pageY;
1835
- L.DomEvent
1836
- .on(document, 'mousemove', this._handleMouseDrag, this)
1837
- .on(document, 'mouseup', this._handleMouseUp, this);
1838
- },
1839
-
1840
- _handleMouseUp: function(e) {
1841
- L.DomEvent.stop(e);
1842
- this.dragging = false;
1843
-
1844
- L.DomEvent
1845
- .off(document, 'mousemove', this._handleMouseDrag, this)
1846
- .off(document, 'mouseup', this._handleMouseUp, this);
1847
- },
1848
-
1849
- _handleMouseDrag: function(e) {
1850
- if (!this.dragging) { return; }
1851
- var deltaX = e.clientX - this.dragstartX;
1852
- this._map.setBearing(deltaX);
1853
- },
1854
-
1855
- _cycleState: function(ev) {
1856
- if (!this._map) {
1857
- return;
1858
- }
1859
-
1860
- var map = this._map;
1861
-
1862
- // Touch mode
1863
- if (!map.touchRotate.enabled() && !map.compassBearing.enabled()) {
1864
- map.touchRotate.enable();
1865
- }
1866
-
1867
- // Compass mode
1868
- else if (!map.compassBearing.enabled()) {
1869
- map.touchRotate.disable();
1870
- map.compassBearing.enable();
1871
- }
1872
-
1873
- // Locked mode
1874
- else {
1875
- map.compassBearing.disable();
1876
- map.setBearing(0);
1877
- if (this.options.closeOnZeroBearing) {
1878
- map.touchRotate.enable();
1879
- }
1880
- }
1881
- this._restyle();
1882
- },
1883
-
1884
- _restyle: function() {
1885
- if (!this._map.options.rotate) {
1886
- L.DomUtil.addClass(this._link, 'leaflet-disabled');
1887
- } else {
1888
- var map = this._map;
1889
- var bearing = map.getBearing();
1890
-
1891
- this._arrow.style.transform = 'rotate(' + bearing + 'deg)';
1892
-
1893
- if (bearing && this.options.closeOnZeroBearing) {
1894
- this._container.style.display = 'block';
1895
- }
1896
-
1897
- // Compass mode
1898
- if (map.compassBearing.enabled()) {
1899
- this._link.style.backgroundColor = 'orange';
1900
- }
1901
-
1902
- // Touch mode
1903
- else if (map.touchRotate.enabled()) {
1904
- this._link.style.backgroundColor = null;
1905
- }
1906
-
1907
- // Locked mode
1908
- else {
1909
- this._link.style.backgroundColor = 'grey';
1910
- if (0 === bearing && this.options.closeOnZeroBearing) {
1911
- this._container.style.display = 'none';
1912
- }
1913
- }
1914
- }
1915
- },
1916
-
1917
- });
1918
-
1919
- L.control.rotate = function(options) {
1920
- return new L.Control.Rotate(options);
1921
- };
1922
-
1923
- L.Map.mergeOptions({
1924
- rotateControl: true,
1925
- });
1926
-
1927
- L.Map.addInitHook(function() {
1928
- if (this.options.rotateControl) {
1929
- var options = typeof this.options.rotateControl === 'object' ? this.options.rotateControl : {};
1930
- this.rotateControl = L.control.rotate(options);
1931
- this.addControl(this.rotateControl);
765
+ _initPanes: function() {
766
+ var panes = this._panes = {};
767
+ this._paneRenderers = {};
768
+ this._mapPane = this.createPane("mapPane", this._container);
769
+ L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
770
+ if (this._rotate) {
771
+ this._rotatePane = this.createPane("rotatePane", this._mapPane);
772
+ this._norotatePane = this.createPane("norotatePane", this._mapPane);
773
+ this.createPane("tilePane", this._rotatePane);
774
+ this.createPane("overlayPane", this._rotatePane);
775
+ this.createPane("shadowPane", this._norotatePane);
776
+ this.createPane("markerPane", this._norotatePane);
777
+ this.createPane("tooltipPane", this._norotatePane);
778
+ this.createPane("popupPane", this._norotatePane);
779
+ } else {
780
+ this.createPane("tilePane");
781
+ this.createPane("overlayPane");
782
+ this.createPane("shadowPane");
783
+ this.createPane("markerPane");
784
+ this.createPane("tooltipPane");
785
+ this.createPane("popupPane");
786
+ }
787
+ if (!this.options.markerZoomAnimation) {
788
+ L.DomUtil.addClass(panes.markerPane, "leaflet-zoom-hide");
789
+ L.DomUtil.addClass(panes.shadowPane, "leaflet-zoom-hide");
790
+ }
791
+ },
792
+ /**
793
+ * Pans the map the minimum amount to make the `latlng` visible. Use
794
+ * padding options to fit the display to more restricted bounds.
795
+ * If `latlng` is already within the (optionally padded) display bounds,
796
+ * the map will not be panned.
797
+ *
798
+ * @see https://github.com/Raruto/leaflet-rotate/issues/18
799
+ *
800
+ * @param {L.LatLng} latlng coordinates
801
+ * @param {Object} [options={}] padding options
802
+ *
803
+ * @returns {L.Map} current map instance
804
+ */
805
+ panInside(latlng, options) {
806
+ if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
807
+ return mapProto.panInside.apply(this, arguments);
808
+ }
809
+ options = options || {};
810
+ const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]), paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]), pixelPoint = this.latLngToContainerPoint(latlng), pixelBounds = L.bounds([L.point(0, 0), this.getSize()]), pixelCenter = pixelBounds.getCenter(), paddedBounds = L.bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]), paddedSize = paddedBounds.getSize();
811
+ if (!paddedBounds.contains(pixelPoint)) {
812
+ this._enforcingBounds = true;
813
+ const centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
814
+ const offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
815
+ pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
816
+ pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
817
+ this.panTo(this.containerPointToLatLng(pixelCenter), options);
818
+ this._enforcingBounds = false;
819
+ }
820
+ return this;
821
+ },
822
+ /**
823
+ * Pans the map to the closest view that would lie inside the given bounds
824
+ * (if it's not already), controlling the animation using the options specific,
825
+ * if any.
826
+ *
827
+ * @TODO check if map bounds calculated by `L.Map::panInsideBounds()`
828
+ * function should match the `rotatePane` or `norotatePane` bounds
829
+ *
830
+ * @see https://github.com/fnicollet/Leaflet/issues/7
831
+ *
832
+ * @param {L.LatLngBounds} bounds coordinates
833
+ * @param {Object} [options] pan options
834
+ * @returns {L.Map} current map instance
835
+ */
836
+ // panInsideBounds: function (bounds, options) {
837
+ // this._enforcingBounds = true;
838
+ // var center = this.getCenter(),
839
+ // newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
840
+ //
841
+ // if (!center.equals(newCenter)) {
842
+ // this.panTo(newCenter, options);
843
+ // }
844
+ //
845
+ // this._enforcingBounds = false;
846
+ // return this;
847
+ // },
848
+ // adjust center for view to get inside bounds
849
+ // _limitCenter(center, zoom, bounds) {
850
+ //
851
+ // if (!bounds) { return center; }
852
+ //
853
+ // const centerPoint = this.project(center, zoom),
854
+ // viewHalf = this.getSize().divideBy(2),
855
+ // viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
856
+ // offset = this._getBoundsOffset(viewBounds, bounds, zoom);
857
+ //
858
+ // // If offset is less than a pixel, ignore.
859
+ // // This prevents unstable projections from getting into
860
+ // // an infinite loop of tiny offsets.
861
+ // if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
862
+ // return center;
863
+ // }
864
+ //
865
+ // return this.unproject(centerPoint.add(offset), zoom);
866
+ // },
867
+ // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
868
+ // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
869
+ // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
870
+ // flyToBounds(bounds, options) {
871
+ // const target = this._getBoundsCenterZoom(bounds, options);
872
+ // return this.flyTo(target.center, target.zoom, options);
873
+ // },
874
+ // _getBoundsCenterZoom(bounds, options) {
875
+ //
876
+ // options = options || {};
877
+ // bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
878
+ //
879
+ // const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
880
+ // paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]);
881
+ //
882
+ // let zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
883
+ //
884
+ // zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
885
+ //
886
+ // if (zoom === Infinity) {
887
+ // return { center: bounds.getCenter(), zoom };
888
+ // }
889
+ //
890
+ // return { center, zoom };
891
+ //
892
+ // },
893
+ /**
894
+ * Returns the maximum zoom level on which the given bounds fit to the map
895
+ * view in its entirety. If `inside` (optional) is set to `true`, the method
896
+ * instead returns the minimum zoom level on which the map view fits into
897
+ * the given bounds in its entirety.
898
+ *
899
+ * @param {L.LatLngBounds} bounds
900
+ * @param {Boolean} [inside=false]
901
+ * @param {L.Point} [padding=[0,0]]
902
+ *
903
+ * @returns {Number} zoom level
904
+ */
905
+ getBoundsZoom(bounds, inside, padding) {
906
+ if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
907
+ return mapProto.getBoundsZoom.apply(this, arguments);
908
+ }
909
+ bounds = L.latLngBounds(bounds);
910
+ padding = L.point(padding || [0, 0]);
911
+ let zoom = this.getZoom() || 0;
912
+ 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
+ zoom = this.getScaleZoom(scale, zoom);
914
+ if (snap) {
915
+ zoom = Math.round(zoom / (snap / 100)) * (snap / 100);
916
+ zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
917
+ }
918
+ return Math.max(min, Math.min(max, zoom));
919
+ },
920
+ /**
921
+ * Layer point of the current center
922
+ *
923
+ * @returns {L.Point} layer center
924
+ */
925
+ // _getCenterLayerPoint: function () {
926
+ // return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
927
+ // },
928
+ /**
929
+ * Offset of the specified place to the current center in pixels
930
+ *
931
+ * @param {L.LatLng} latlng map coordinates
932
+ */
933
+ _getCenterOffset: function(latlng) {
934
+ var centerOffset = mapProto._getCenterOffset.apply(this, arguments);
935
+ if (this._rotate) {
936
+ centerOffset = centerOffset.rotate(this._bearing);
937
+ }
938
+ return centerOffset;
939
+ },
940
+ /**
941
+ * @since leaflet-rotate (v0.1)
942
+ */
943
+ _getRotatePanePos: function() {
944
+ return this._rotatePanePos || new L.Point(0, 0);
945
+ },
946
+ // _latLngToNewLayerPoint(latlng, zoom, center) {
947
+ // const topLeft = this._getNewPixelOrigin(center, zoom);
948
+ // return this.project(latlng, zoom)._subtract(topLeft);
949
+ //},
950
+ _getNewPixelOrigin: function(center, zoom) {
951
+ if (!this._rotate) {
952
+ return mapProto._getNewPixelOrigin.apply(this, arguments);
953
+ }
954
+ var viewHalf = this.getSize()._divideBy(2);
955
+ return this.project(center, zoom).rotate(this._bearing)._subtract(viewHalf)._add(this._getMapPanePos())._add(this._getRotatePanePos()).rotate(-this._bearing)._round();
956
+ },
957
+ /**
958
+ * @since leaflet-rotate (v0.2)
959
+ *
960
+ * @see src\layer\tile\GridLayer::_getTiledPixelBounds()
961
+ */
962
+ _getNewPixelBounds: function(center, zoom) {
963
+ center = center || this.getCenter();
964
+ zoom = zoom || this.getZoom();
965
+ if (!this._rotate && mapProto._getNewPixelBounds) {
966
+ return mapProto._getNewPixelBounds.apply(this, arguments);
967
+ }
968
+ var 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
+ this.containerPointToLayerPoint([0, 0]).floor(),
970
+ this.containerPointToLayerPoint([size.x, 0]).floor(),
971
+ this.containerPointToLayerPoint([0, size.y]).floor(),
972
+ this.containerPointToLayerPoint([size.x, size.y]).floor()
973
+ ]).getSize().divideBy(scale * 2);
974
+ return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
975
+ },
976
+ /**
977
+ * @since leaflet-rotate (v0.2)
978
+ *
979
+ * @return {L.Point} map pivot point (center)
980
+ */
981
+ _getPixelCenter: function() {
982
+ if (!this._rotate && mapProto._getPixelCenter) {
983
+ return mapProto._getPixelCenter.apply(this, arguments);
984
+ }
985
+ return this.getSize()._divideBy(2)._subtract(this._getMapPanePos());
986
+ },
987
+ /**
988
+ * @since leaflet-rotate (v0.2)
989
+ *
990
+ * @see src\layer\vector\Renderer::_update()
991
+ */
992
+ _getPaddedPixelBounds: function(padding) {
993
+ if (!this._rotate && mapProto._getPaddedPixelBounds) {
994
+ return mapProto._getPaddedPixelBounds.apply(this, arguments);
995
+ }
996
+ var p = padding, size = this.getSize(), padMin = size.multiplyBy(-p), padMax = size.multiplyBy(1 + p);
997
+ return new L.Bounds([
998
+ this.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),
999
+ this.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),
1000
+ this.containerPointToLayerPoint([padMax.x, padMin.y]).floor(),
1001
+ this.containerPointToLayerPoint([padMax.x, padMax.y]).floor()
1002
+ ]);
1003
+ },
1004
+ _handleGeolocationResponse: function(pos) {
1005
+ if (!this._container._leaflet_id) {
1006
+ return;
1007
+ }
1008
+ var 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
+ if (options.setView) {
1010
+ var zoom = this.getBoundsZoom(bounds);
1011
+ this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
1012
+ }
1013
+ var data = {
1014
+ latlng,
1015
+ bounds,
1016
+ timestamp: pos.timestamp,
1017
+ /** @TODO use mapProto._handleGeolocationResponse */
1018
+ heading: hdg
1019
+ };
1020
+ for (var i in pos.coords) {
1021
+ if (typeof pos.coords[i] === "number") {
1022
+ data[i] = pos.coords[i];
1932
1023
  }
1933
- });
1024
+ }
1025
+ this.fire("locationfound", data);
1026
+ }
1027
+ /**
1028
+ * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/geo/LatLngBounds.js#L253-L264
1029
+ *
1030
+ * @param {L.Bounds} points
1031
+ * @returns {L.Bounds}
1032
+ */
1033
+ // toCircumscribedBounds(points) {
1034
+ // var minX = points.reduce(function (pv, v) { return Math.min(pv, v.x); }, points[0].x),
1035
+ // maxX = points.reduce(function (pv, v) { return Math.max(pv, v.x); }, points[0].x),
1036
+ // minY = points.reduce(function (pv, v) { return Math.min(pv, v.y); }, points[0].y),
1037
+ // maxY = points.reduce(function (pv, v) { return Math.max(pv, v.y); }, points[0].y);
1038
+ //
1039
+ // return L.bounds(L.point(minX, minY), L.point(maxX, maxY));
1040
+ // },
1041
+ });
1042
+
1043
+ // src/map/handler/CompassBearing.js
1044
+ L.Map.CompassBearing = L.Handler.extend({
1045
+ initialize: function(map) {
1046
+ this._map = map;
1047
+ if ("ondeviceorientationabsolute" in window) {
1048
+ this.__deviceOrientationEvent = "deviceorientationabsolute";
1049
+ } else if ("ondeviceorientation" in window) {
1050
+ this.__deviceOrientationEvent = "deviceorientation";
1051
+ }
1052
+ this._throttled = L.Util.throttle(this._onDeviceOrientation, 100, this);
1053
+ },
1054
+ addHooks: function() {
1055
+ if (this._map._rotate && this.__deviceOrientationEvent) {
1056
+ L.DomEvent.on(window, this.__deviceOrientationEvent, this._throttled, this);
1057
+ } else {
1058
+ this.disable();
1059
+ }
1060
+ },
1061
+ removeHooks: function() {
1062
+ if (this._map._rotate && this.__deviceOrientationEvent) {
1063
+ L.DomEvent.off(window, this.__deviceOrientationEvent, this._throttled, this);
1064
+ }
1065
+ },
1066
+ /**
1067
+ * `DeviceOrientationEvent.absolute` - Indicates whether the device is providing absolute
1068
+ * orientation values (relatives to Magnetic North) or
1069
+ * using some arbitrary frame determined by the device.
1070
+ *
1071
+ * `DeviceOrientationEvent.alpha` - Returns the rotation of the device around the Z axis;
1072
+ * that is, the number of degrees by which the device is
1073
+ * being twisted around the center of the screen.
1074
+ *
1075
+ * `window.orientation` - Returns the screen orientation in degrees (in 90-degree increments)
1076
+ * of the viewport relative to the device's natural orientation.
1077
+ * Its only possible values are -90, 0, 90, and 180. Positive
1078
+ * values are counterclockwise; negative values are clockwise.
1079
+ *
1080
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/absolute
1081
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/alpha
1082
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation
1083
+ */
1084
+ _onDeviceOrientation: function(e) {
1085
+ var angle = e.webkitCompassHeading != null ? e.webkitCompassHeading : e.alpha;
1086
+ var deviceOrientation = 0;
1087
+ if (!Number.isFinite(angle)) {
1088
+ return;
1089
+ }
1090
+ if (!e.absolute && e.webkitCompassHeading) {
1091
+ angle = 360 - angle;
1092
+ }
1093
+ if (!e.absolute && "undefined" !== typeof window.orientation) {
1094
+ deviceOrientation = window.orientation;
1095
+ }
1096
+ this._map.setBearing(angle - deviceOrientation);
1097
+ }
1098
+ });
1099
+ L.Map.addInitHook("addHandler", "compassBearing", L.Map.CompassBearing);
1100
+
1101
+ // src/map/handler/ContainerMutation.js
1102
+ L.Map.mergeOptions({
1103
+ /**
1104
+ * Whether the map uses mutation observers to
1105
+ * detect changes in its container and trigger
1106
+ * `invalidateSize`. Disabled by default due to
1107
+ * support not being available in all web browsers.
1108
+ *
1109
+ * @type {Boolean}
1110
+ *
1111
+ * @see https://developer.mozilla.org/docs/Web/API/MutationObserver
1112
+ */
1113
+ trackContainerMutation: false
1114
+ });
1115
+ L.Map.ContainerMutation = L.Handler.extend({
1116
+ addHooks: function() {
1117
+ if (!this._observer) {
1118
+ this._observer = new MutationObserver(L.Util.bind(this._map.invalidateSize, this._map));
1119
+ }
1120
+ this._observer.observe(this._map.getContainer(), {
1121
+ childList: false,
1122
+ attributes: true,
1123
+ characterData: false,
1124
+ subtree: false,
1125
+ attributeFilter: ["style"]
1126
+ });
1127
+ },
1128
+ removeHooks: function() {
1129
+ this._observer.disconnect();
1130
+ }
1131
+ });
1132
+ L.Map.addInitHook("addHandler", "trackContainerMutation", L.Map.ContainerMutation);
1133
+
1134
+ // src/map/handler/TouchGestures.js
1135
+ L.Map.mergeOptions({
1136
+ /**
1137
+ * Set it to false if you don't want the map to
1138
+ * zoom beyond min/max zoom and then bounce back
1139
+ * when pinch-zooming.
1140
+ *
1141
+ * @type {Boolean}
1142
+ */
1143
+ bounceAtZoomLimits: true
1144
+ });
1145
+ L.Map.TouchGestures = L.Handler.extend({
1146
+ initialize: function(map) {
1147
+ this._map = map;
1148
+ this.rotate = !!this._map.options.touchRotate;
1149
+ this.zoom = !!this._map.options.touchZoom;
1150
+ },
1151
+ addHooks: function() {
1152
+ L.DomEvent.on(this._map._container, "touchstart", this._onTouchStart, this);
1153
+ },
1154
+ removeHooks: function() {
1155
+ L.DomEvent.off(this._map._container, "touchstart", this._onTouchStart, this);
1156
+ },
1157
+ _onTouchStart: function(e) {
1158
+ var map = this._map;
1159
+ if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming || this._rotating) {
1160
+ return;
1161
+ }
1162
+ var p1 = map.mouseEventToContainerPoint(e.touches[0]), p2 = map.mouseEventToContainerPoint(e.touches[1]), vector = p1.subtract(p2);
1163
+ this._centerPoint = map.getSize()._divideBy(2);
1164
+ this._startLatLng = map.containerPointToLatLng(this._centerPoint);
1165
+ if (this.zoom) {
1166
+ if (map.options.touchZoom !== "center") {
1167
+ this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
1168
+ }
1169
+ this._startDist = p1.distanceTo(p2);
1170
+ this._startZoom = map.getZoom();
1171
+ this._zooming = true;
1172
+ } else {
1173
+ this._zooming = false;
1174
+ }
1175
+ if (this.rotate) {
1176
+ this._startTheta = Math.atan(vector.x / vector.y);
1177
+ this._startBearing = map.getBearing();
1178
+ if (vector.y < 0) {
1179
+ this._startBearing += 180;
1180
+ }
1181
+ this._rotating = true;
1182
+ } else {
1183
+ this._rotating = false;
1184
+ }
1185
+ this._moved = false;
1186
+ map._stop();
1187
+ L.DomEvent.on(document, "touchmove", this._onTouchMove, this).on(document, "touchend touchcancel", this._onTouchEnd, this);
1188
+ L.DomEvent.preventDefault(e);
1189
+ },
1190
+ _onTouchMove: function(e) {
1191
+ if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) {
1192
+ return;
1193
+ }
1194
+ var map = this._map, p1 = map.mouseEventToContainerPoint(e.touches[0]), p2 = map.mouseEventToContainerPoint(e.touches[1]), vector = p1.subtract(p2), scale = p1.distanceTo(p2) / this._startDist, delta;
1195
+ if (this._rotating) {
1196
+ var theta = Math.atan(vector.x / vector.y);
1197
+ var bearingDelta = (theta - this._startTheta) * L.DomUtil.RAD_TO_DEG;
1198
+ if (vector.y < 0) {
1199
+ bearingDelta += 180;
1200
+ }
1201
+ if (bearingDelta) {
1202
+ map.setBearing(this._startBearing - bearingDelta);
1203
+ }
1204
+ }
1205
+ if (this._zooming) {
1206
+ this._zoom = map.getScaleZoom(scale, this._startZoom);
1207
+ if (!map.options.bounceAtZoomLimits && (this._zoom < map.getMinZoom() && scale < 1 || this._zoom > map.getMaxZoom() && scale > 1)) {
1208
+ this._zoom = map._limitZoom(this._zoom);
1209
+ }
1210
+ if (map.options.touchZoom === "center") {
1211
+ this._center = this._startLatLng;
1212
+ if (scale === 1) {
1213
+ return;
1214
+ }
1215
+ } else {
1216
+ delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
1217
+ if (scale === 1 && delta.x === 0 && delta.y === 0) {
1218
+ return;
1219
+ }
1220
+ var alpha = -map.getBearing() * L.DomUtil.DEG_TO_RAD;
1221
+ this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(delta.rotate(alpha)));
1222
+ }
1223
+ }
1224
+ if (!this._moved) {
1225
+ map._moveStart(true, false);
1226
+ this._moved = true;
1227
+ }
1228
+ L.Util.cancelAnimFrame(this._animRequest);
1229
+ var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, void 0);
1230
+ this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
1231
+ if (this.zoom) {
1232
+ if (map.options.zoomAnimation) {
1233
+ map._animateZoomNoDelay(this._center, map._limitZoom(this._zoom));
1234
+ } else {
1235
+ map._resetView(this._center, map._limitZoom(this._zoom));
1236
+ }
1237
+ }
1238
+ L.DomEvent.preventDefault(e);
1239
+ },
1240
+ _onTouchEnd: function() {
1241
+ if (!this._moved || !(this._zooming || this._rotating)) {
1242
+ this._zooming = false;
1243
+ return;
1244
+ }
1245
+ this._zooming = false;
1246
+ this._rotating = false;
1247
+ L.Util.cancelAnimFrame(this._animRequest);
1248
+ L.DomEvent.off(document, "touchmove", this._onTouchMove, this).off(document, "touchend touchcancel", this._onTouchEnd, this);
1249
+ if (this.zoom) {
1250
+ if (this._map.options.zoomAnimation) {
1251
+ this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
1252
+ } else {
1253
+ this._map._resetView(this._center, this._map._limitZoom(this._zoom));
1254
+ }
1255
+ }
1256
+ }
1257
+ });
1258
+ L.Map.addInitHook("addHandler", "touchGestures", L.Map.TouchGestures);
1934
1259
 
1935
- }));
1260
+ // src/map/handler/TouchRotate.js
1261
+ L.Map.mergeOptions({
1262
+ /**
1263
+ * Whether the map can be rotated with a two-finger rotation gesture
1264
+ *
1265
+ * @type {Boolean}
1266
+ */
1267
+ touchRotate: false
1268
+ });
1269
+ L.Map.TouchRotate = L.Handler.extend({
1270
+ addHooks: function() {
1271
+ this._map.touchGestures.enable();
1272
+ this._map.touchGestures.rotate = true;
1273
+ },
1274
+ removeHooks: function() {
1275
+ this._map.touchGestures.rotate = false;
1276
+ }
1277
+ });
1278
+ L.Map.addInitHook("addHandler", "touchRotate", L.Map.TouchRotate);
1279
+
1280
+ // src/map/handler/ShiftKeyRotate.js
1281
+ L.Map.mergeOptions({
1282
+ /**
1283
+ * Whether the map can be rotated with shift + wheel scroll
1284
+ * @type {Boolean}
1285
+ */
1286
+ shiftKeyRotate: true
1287
+ });
1288
+ L.Map.ShiftKeyRotate = L.Handler.extend({
1289
+ addHooks: function() {
1290
+ L.DomEvent.on(this._map._container, "wheel", this._handleShiftScroll, this);
1291
+ this._map.shiftKeyRotate.rotate = true;
1292
+ },
1293
+ removeHooks: function() {
1294
+ L.DomEvent.off(this._map._container, "wheel", this._handleShiftScroll, this);
1295
+ this._map.shiftKeyRotate.rotate = false;
1296
+ },
1297
+ _handleShiftScroll: function(e) {
1298
+ if (e.shiftKey) {
1299
+ e.preventDefault();
1300
+ this._map.scrollWheelZoom.disable();
1301
+ this._map.setBearing(this._map._bearing * L.DomUtil.RAD_TO_DEG + Math.sign(e.deltaY) * 5);
1302
+ } else {
1303
+ this._map.scrollWheelZoom.enable();
1304
+ }
1305
+ }
1306
+ });
1307
+ L.Map.addInitHook("addHandler", "shiftKeyRotate", L.Map.ShiftKeyRotate);
1308
+ L.Map.addInitHook(function() {
1309
+ if (this.scrollWheelZoom.enabled() && this.shiftKeyRotate.enabled()) {
1310
+ this.scrollWheelZoom.disable();
1311
+ this.scrollWheelZoom.enable();
1312
+ }
1313
+ });
1314
+
1315
+ // src/map/handler/TouchZoom.js
1316
+ L.Map.mergeOptions({
1317
+ /**
1318
+ * Whether the map can be zoomed by touch-dragging
1319
+ * with two fingers. If passed `'center'`, it will
1320
+ * zoom to the center of the view regardless of
1321
+ * where the touch events (fingers) were. Enabled
1322
+ * for touch-capable web browsers.
1323
+ *
1324
+ * @type {(Boolean|String)}
1325
+ */
1326
+ touchZoom: L.Browser.touch,
1327
+ /**
1328
+ * @TODO check if this is a duplicate of `L.Map.TouchGestures::bounceAtZoomLimits`
1329
+ *
1330
+ * Set it to false if you don't want the map to
1331
+ * zoom beyond min/max zoom and then bounce back
1332
+ * when pinch-zooming.
1333
+ *
1334
+ * @type {Boolean}
1335
+ */
1336
+ bounceAtZoomLimits: false
1337
+ });
1338
+ L.Map.TouchZoom = L.Handler.extend({
1339
+ addHooks: function() {
1340
+ L.DomUtil.addClass(this._map._container, "leaflet-touch-zoom");
1341
+ this._map.touchGestures.enable();
1342
+ this._map.touchGestures.zoom = true;
1343
+ },
1344
+ removeHooks: function() {
1345
+ L.DomUtil.removeClass(this._map._container, "leaflet-touch-zoom");
1346
+ this._map.touchGestures.zoom = false;
1347
+ }
1348
+ });
1349
+ L.Map.addInitHook("addHandler", "touchZoom", L.Map.TouchZoom);
1350
+
1351
+ // src/control/Rotate.js
1352
+ L.Control.Rotate = L.Control.extend({
1353
+ options: {
1354
+ position: "topleft",
1355
+ closeOnZeroBearing: true
1356
+ },
1357
+ onAdd: function(map) {
1358
+ var container = this._container = L.DomUtil.create("div", "leaflet-control-rotate leaflet-bar");
1359
+ var arrow = this._arrow = L.DomUtil.create("span", "leaflet-control-rotate-arrow");
1360
+ 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
+ arrow.style.cursor = "grab";
1362
+ arrow.style.display = "block";
1363
+ arrow.style.width = "100%";
1364
+ arrow.style.height = "100%";
1365
+ arrow.style.backgroundRepeat = "no-repeat";
1366
+ arrow.style.backgroundPosition = "50%";
1367
+ var link = this._link = L.DomUtil.create("a", "leaflet-control-rotate-toggle", container);
1368
+ link.appendChild(arrow);
1369
+ link.href = "#";
1370
+ link.title = "Rotate map";
1371
+ 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
+ this._restyle();
1376
+ map.on("rotate", this._restyle, this);
1377
+ this._follow = false;
1378
+ this._canFollow = false;
1379
+ if (this.options.closeOnZeroBearing && map.getBearing() === 0) {
1380
+ container.style.display = "none";
1381
+ }
1382
+ return container;
1383
+ },
1384
+ onRemove: function(map) {
1385
+ map.off("rotate", this._restyle, this);
1386
+ },
1387
+ _handleMouseDown: function(e) {
1388
+ L.DomEvent.stop(e);
1389
+ this.dragging = true;
1390
+ this.dragstartX = e.pageX;
1391
+ this.dragstartY = e.pageY;
1392
+ L.DomEvent.on(document, "mousemove", this._handleMouseDrag, this).on(document, "mouseup", this._handleMouseUp, this);
1393
+ },
1394
+ _handleMouseUp: function(e) {
1395
+ L.DomEvent.stop(e);
1396
+ this.dragging = false;
1397
+ L.DomEvent.off(document, "mousemove", this._handleMouseDrag, this).off(document, "mouseup", this._handleMouseUp, this);
1398
+ },
1399
+ _handleMouseDrag: function(e) {
1400
+ if (!this.dragging) {
1401
+ return;
1402
+ }
1403
+ var deltaX = e.clientX - this.dragstartX;
1404
+ this._map.setBearing(deltaX);
1405
+ },
1406
+ _cycleState: function(ev) {
1407
+ if (!this._map) {
1408
+ return;
1409
+ }
1410
+ var map = this._map;
1411
+ if (!map.touchRotate.enabled() && !map.compassBearing.enabled()) {
1412
+ map.touchRotate.enable();
1413
+ } else if (!map.compassBearing.enabled()) {
1414
+ map.touchRotate.disable();
1415
+ (DeviceOrientationEvent && DeviceOrientationEvent.requestPermission ? DeviceOrientationEvent.requestPermission() : Promise.resolve("granted")).then((state) => "granted" === state && map.compassBearing.enable());
1416
+ } else {
1417
+ map.compassBearing.disable();
1418
+ map.setBearing(0);
1419
+ if (this.options.closeOnZeroBearing) {
1420
+ map.touchRotate.enable();
1421
+ }
1422
+ }
1423
+ this._restyle();
1424
+ },
1425
+ _restyle: function() {
1426
+ if (!this._map.options.rotate) {
1427
+ L.DomUtil.addClass(this._link, "leaflet-disabled");
1428
+ } else {
1429
+ var map = this._map;
1430
+ var bearing = map.getBearing();
1431
+ this._arrow.style.transform = "rotate(" + bearing + "deg)";
1432
+ if (bearing && this.options.closeOnZeroBearing) {
1433
+ this._container.style.display = "block";
1434
+ }
1435
+ if (map.compassBearing.enabled()) {
1436
+ this._link.style.backgroundColor = "orange";
1437
+ } else if (map.touchRotate.enabled()) {
1438
+ this._link.style.backgroundColor = null;
1439
+ } else {
1440
+ this._link.style.backgroundColor = "grey";
1441
+ if (0 === bearing && this.options.closeOnZeroBearing) {
1442
+ this._container.style.display = "none";
1443
+ }
1444
+ }
1445
+ }
1446
+ }
1447
+ });
1448
+ L.control.rotate = function(options) {
1449
+ return new L.Control.Rotate(options);
1450
+ };
1451
+ L.Map.mergeOptions({
1452
+ rotateControl: true
1453
+ });
1454
+ L.Map.addInitHook(function() {
1455
+ if (this.options.rotateControl) {
1456
+ var options = typeof this.options.rotateControl === "object" ? this.options.rotateControl : {};
1457
+ this.rotateControl = L.control.rotate(options);
1458
+ this.addControl(this.rotateControl);
1459
+ }
1460
+ });
1461
+ return __toCommonJS(index_exports);
1462
+ })();
1936
1463
  //# sourceMappingURL=leaflet-rotate-src.js.map