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