leaflet-rotate 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/server.js +19 -3
- package/dist/leaflet-rotate-src.js +229 -75
- package/dist/leaflet-rotate-src.js.map +4 -4
- package/dist/leaflet-rotate.js +1 -1
- package/dist/leaflet-rotate.js.map +4 -4
- package/lib/debug.js +3 -0
- package/lib/debug.min.js +1 -1
- package/package.json +1 -1
- package/src/dom/Draggable.js +20 -8
- package/src/index.js +1 -0
- package/src/layer/marker/Marker.js +24 -11
- package/src/layer/tile/GridLayer.js +16 -1
- package/src/map/Map.js +154 -60
- package/src/map/handler/Drag.js +59 -0
- package/test/features/example.feature +5 -0
- package/test/features/harness.feature +84 -0
- package/test/features/navigation-bounds.feature +33 -0
- package/test/features/overlays.feature +25 -0
- package/test/features/pitch.feature +19 -0
- package/test/index.html +90 -11
- package/test/steps/example.steps.js +18 -0
- package/test/steps/harness.steps.js +207 -0
- package/test/steps/navigation-bounds.steps.js +195 -0
- package/test/steps/overlays.steps.js +186 -0
- package/test/steps/pitch.steps.js +264 -0
- package/test/steps/rotation.steps.js +28 -4
- package/test/support/hooks.js +20 -8
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Load Leaflet first, then the plugin. For example, using the published CDN build:
|
|
|
26
26
|
```html
|
|
27
27
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css">
|
|
28
28
|
<script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
|
|
29
|
-
<script src="https://unpkg.com/leaflet-rotate@0.5.
|
|
29
|
+
<script src="https://unpkg.com/leaflet-rotate@0.5.4/dist/leaflet-rotate.js"></script>
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Or if you want to test it with [`2.0.0-alpha.1`](https://github.com/Leaflet/Leaflet/releases/tag/v2.0.0-alpha.1):
|
|
@@ -35,7 +35,7 @@ Or if you want to test it with [`2.0.0-alpha.1`](https://github.com/Leaflet/Leaf
|
|
|
35
35
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@2.0.0-alpha.1/dist/leaflet.css">
|
|
36
36
|
<script src="https://unpkg.com/leaflet@2.0.0-alpha.1/dist/leaflet-global-src.js"></script>
|
|
37
37
|
<script>globalThis.L = globalThis.leaflet;</script>
|
|
38
|
-
<script src="https://unpkg.com/leaflet-rotate@0.5.
|
|
38
|
+
<script src="https://unpkg.com/leaflet-rotate@0.5.4/dist/leaflet-rotate.js"></script>
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
Now, you can enable rotation in the map options. Bearing and pitch values are in degrees:
|
package/build/server.js
CHANGED
|
@@ -72,11 +72,27 @@ export function startServer(port = 8080) {
|
|
|
72
72
|
response.writeHead(404).end();
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
const headers = {
|
|
76
|
+
'Accept-Ranges': 'bytes',
|
|
76
77
|
'Content-Type': types[extname(target).toLowerCase()] ?? 'application/octet-stream',
|
|
77
|
-
}
|
|
78
|
+
};
|
|
79
|
+
// Serve a single byte range per request.
|
|
80
|
+
const range = /^bytes=(\d*)-(\d*)$/.exec(request.headers.range ?? '');
|
|
81
|
+
let start = 0;
|
|
82
|
+
let end = content.size - 1;
|
|
83
|
+
if (range && (range[1] || range[2])) {
|
|
84
|
+
start = range[1] ? Number(range[1]) : Math.max(0, content.size - Number(range[2]));
|
|
85
|
+
end = range[1] && range[2] ? Math.min(Number(range[2]), end) : end;
|
|
86
|
+
if (start > end) {
|
|
87
|
+
response.writeHead(416, { 'Content-Range': `bytes */${content.size}` }).end();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
response.writeHead(206, { ...headers, 'Content-Length': end - start + 1, 'Content-Range': `bytes ${start}-${end}/${content.size}` });
|
|
91
|
+
} else {
|
|
92
|
+
response.writeHead(200, { ...headers, 'Content-Length': content.size });
|
|
93
|
+
}
|
|
78
94
|
if (request.method === 'HEAD') response.end();
|
|
79
|
-
else createReadStream(target).on('error', error => {
|
|
95
|
+
else createReadStream(target, content.size ? { start, end } : undefined).on('error', error => {
|
|
80
96
|
console.error(error);
|
|
81
97
|
response.destroy(error);
|
|
82
98
|
}).pipe(response);
|
|
@@ -84,11 +84,25 @@ var leafletRotate = (() => {
|
|
|
84
84
|
});
|
|
85
85
|
|
|
86
86
|
// src/dom/Draggable.js
|
|
87
|
+
var draggableProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Draggable.prototype));
|
|
87
88
|
L.Draggable.include({
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
_onMove(e) {
|
|
90
|
+
const pointer = e.touches ? e.touches[0] : e;
|
|
91
|
+
if (this._enabled && this._moving && (!e.touches || e.touches.length === 1)) {
|
|
92
|
+
const offset = new L.Point(pointer.clientX, pointer.clientY).subtract(this._startPoint);
|
|
93
|
+
if (!offset.x && !offset.y || Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) {
|
|
94
|
+
L.DomEvent.preventDefault(e);
|
|
95
|
+
this._newPos = this._startPos.add(new L.Point(
|
|
96
|
+
offset.x / this._parentScale.x,
|
|
97
|
+
offset.y / this._parentScale.y
|
|
98
|
+
));
|
|
99
|
+
this._lastEvent = e;
|
|
100
|
+
this._updatePosition();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return draggableProto._onMove.apply(this, arguments);
|
|
105
|
+
}
|
|
92
106
|
});
|
|
93
107
|
|
|
94
108
|
// src/geometry/Point.js
|
|
@@ -478,6 +492,24 @@ var leafletRotate = (() => {
|
|
|
478
492
|
if (this._map._rotate) {
|
|
479
493
|
pos = this._map.rotatedPointToMapPanePoint(pos);
|
|
480
494
|
}
|
|
495
|
+
const bearing = this._getIconBearing();
|
|
496
|
+
if (this._icon) {
|
|
497
|
+
L.DomUtil.setPosition(this._icon, pos, bearing, pos, this.options.scale);
|
|
498
|
+
}
|
|
499
|
+
if (this._shadow) {
|
|
500
|
+
L.DomUtil.setPosition(this._shadow, pos, bearing, pos, this.options.scale);
|
|
501
|
+
}
|
|
502
|
+
this._zIndex = pos.y + this.options.zIndexOffset;
|
|
503
|
+
this._resetZIndex();
|
|
504
|
+
},
|
|
505
|
+
/**
|
|
506
|
+
* Compute the on-screen icon rotation from `rotation`, `rotateWithView` and `keepUpright`.
|
|
507
|
+
*
|
|
508
|
+
* @returns {number} Angle in radians.
|
|
509
|
+
*
|
|
510
|
+
* @since leaflet-rotate (0.5.3)
|
|
511
|
+
*/
|
|
512
|
+
_getIconBearing() {
|
|
481
513
|
let bearing = this.options.rotation || 0;
|
|
482
514
|
if (this.options.rotateWithView) {
|
|
483
515
|
bearing += this._map._bearing;
|
|
@@ -489,14 +521,7 @@ var leafletRotate = (() => {
|
|
|
489
521
|
}
|
|
490
522
|
bearing %= 2 * Math.PI;
|
|
491
523
|
}
|
|
492
|
-
|
|
493
|
-
L.DomUtil.setPosition(this._icon, pos, bearing, pos, this.options.scale);
|
|
494
|
-
}
|
|
495
|
-
if (this._shadow) {
|
|
496
|
-
L.DomUtil.setPosition(this._shadow, pos, bearing, pos, this.options.scale);
|
|
497
|
-
}
|
|
498
|
-
this._zIndex = pos.y + this.options.zIndexOffset;
|
|
499
|
-
this._resetZIndex();
|
|
524
|
+
return bearing;
|
|
500
525
|
},
|
|
501
526
|
// _updateZIndex(offset) {
|
|
502
527
|
// if (!this._map._rotate) {
|
|
@@ -541,9 +566,24 @@ var leafletRotate = (() => {
|
|
|
541
566
|
}
|
|
542
567
|
return events;
|
|
543
568
|
},
|
|
569
|
+
/**
|
|
570
|
+
* Preserve the 3D context on tile containers without making individual tile levels 3D.
|
|
571
|
+
*
|
|
572
|
+
* @since leaflet-rotate (0.5.3)
|
|
573
|
+
*/
|
|
574
|
+
_initContainer() {
|
|
575
|
+
gridLayerProto._initContainer.apply(this, arguments);
|
|
576
|
+
if (!this._map._rotate || !this._container) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
for (let pane = this._container; pane && pane !== this._map._rotatePane; pane = pane.parentElement) {
|
|
580
|
+
pane.style.transformStyle = "preserve-3d";
|
|
581
|
+
}
|
|
582
|
+
},
|
|
544
583
|
/**
|
|
545
584
|
* Keep tile levels aligned when a bearing/pitch change shifts the pixel origin
|
|
546
585
|
* (eg. when the rotation pivot is not the view center), then refresh tiles (throttled).
|
|
586
|
+
* The first event also synchronizes levels created before any accumulated pans.
|
|
547
587
|
*
|
|
548
588
|
* @listens L.Map~rotate
|
|
549
589
|
*
|
|
@@ -555,7 +595,7 @@ var leafletRotate = (() => {
|
|
|
555
595
|
return;
|
|
556
596
|
}
|
|
557
597
|
const origin = map.getPixelOrigin();
|
|
558
|
-
if (this._rotateOrigin
|
|
598
|
+
if ((!this._rotateOrigin || !this._rotateOrigin.equals(origin)) && !map._animatingZoom && this._levels) {
|
|
559
599
|
this._setZoomTransforms(map.getCenter(), map.getZoom());
|
|
560
600
|
}
|
|
561
601
|
this._rotateOrigin = origin;
|
|
@@ -796,6 +836,19 @@ var leafletRotate = (() => {
|
|
|
796
836
|
pitchMaxDistance: 3
|
|
797
837
|
});
|
|
798
838
|
L.Map.include({
|
|
839
|
+
/**
|
|
840
|
+
* Rebuild perspective before Leaflet calculates the origin of a reset view.
|
|
841
|
+
* A reset clears the outer pane translation, unlike a completed pan.
|
|
842
|
+
*/
|
|
843
|
+
_resetView() {
|
|
844
|
+
if (this._pitch) {
|
|
845
|
+
delete this._pitchPan;
|
|
846
|
+
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
|
|
847
|
+
this._updatePitchTransform();
|
|
848
|
+
arguments[0] = this._limitCenter(arguments[0], arguments[1], this.options.maxBounds);
|
|
849
|
+
}
|
|
850
|
+
return mapProto._resetView.apply(this, arguments);
|
|
851
|
+
},
|
|
799
852
|
/**
|
|
800
853
|
* Apply an immediate animated-zoom frame without waiting for a transition delay.
|
|
801
854
|
*
|
|
@@ -835,6 +888,7 @@ var leafletRotate = (() => {
|
|
|
835
888
|
this.on("move", this._updatePitchPan, this);
|
|
836
889
|
this.on("moveend", this._endPitchPan, this);
|
|
837
890
|
this.on("move", this._updatePitchSky, this);
|
|
891
|
+
this.on("rotate", this._panInsideMaxBounds, this);
|
|
838
892
|
this.setBearing(this.options.bearing);
|
|
839
893
|
this.setPitch(this.options.pitch);
|
|
840
894
|
}
|
|
@@ -1180,22 +1234,32 @@ var leafletRotate = (() => {
|
|
|
1180
1234
|
* @param {L.Point|number[]} point Input pixel coordinate.
|
|
1181
1235
|
* @param {DOMMatrix|Object} matrix Forward CSS matrix or inverse homography with
|
|
1182
1236
|
* row-major coefficients `a` through `i`.
|
|
1237
|
+
* @param {L.Point} [anchor] Keep inverse pan coordinates on this anchor's side of the horizon.
|
|
1183
1238
|
*
|
|
1184
1239
|
* @returns {L.Point} Transformed pixel coordinate.
|
|
1185
1240
|
*
|
|
1186
1241
|
* @since leaflet-rotate (0.5.0)
|
|
1187
1242
|
*/
|
|
1188
|
-
_transformPitchPoint(point, matrix) {
|
|
1243
|
+
_transformPitchPoint(point, matrix, anchor) {
|
|
1189
1244
|
point = toPoint(point);
|
|
1190
1245
|
if (!(matrix instanceof DOMMatrix)) {
|
|
1191
|
-
|
|
1246
|
+
if (anchor) {
|
|
1247
|
+
const anchorW = matrix.g * anchor.x + matrix.h * anchor.y + matrix.i, pointW = matrix.g * point.x + matrix.h * point.y + matrix.i;
|
|
1248
|
+
if (pointW * anchorW <= 0) {
|
|
1249
|
+
point = anchor.add(point.subtract(anchor).multiplyBy(anchorW / (anchorW - pointW) * (1 - 1e-6)));
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
const denominator2 = matrix.g * point.x + matrix.h * point.y + matrix.i;
|
|
1192
1253
|
return new L.Point(
|
|
1193
|
-
(matrix.a * point.x + matrix.b * point.y + matrix.c) /
|
|
1194
|
-
(matrix.d * point.x + matrix.e * point.y + matrix.f) /
|
|
1254
|
+
(matrix.a * point.x + matrix.b * point.y + matrix.c) / denominator2,
|
|
1255
|
+
(matrix.d * point.x + matrix.e * point.y + matrix.f) / denominator2
|
|
1195
1256
|
);
|
|
1196
1257
|
}
|
|
1197
|
-
const
|
|
1198
|
-
return new L.Point(
|
|
1258
|
+
const denominator = matrix.m14 * point.x + matrix.m24 * point.y + matrix.m44;
|
|
1259
|
+
return new L.Point(
|
|
1260
|
+
(matrix.m11 * point.x + matrix.m21 * point.y + matrix.m41) / denominator,
|
|
1261
|
+
(matrix.m12 * point.x + matrix.m22 * point.y + matrix.m42) / denominator
|
|
1262
|
+
);
|
|
1199
1263
|
},
|
|
1200
1264
|
/**
|
|
1201
1265
|
* Apply the current pitch/bearing CSS transform and cache its forward and inverse matrices.
|
|
@@ -1222,8 +1286,10 @@ var leafletRotate = (() => {
|
|
|
1222
1286
|
*/
|
|
1223
1287
|
_setPitchTransform(transform) {
|
|
1224
1288
|
this._rotatePane.style.transformOrigin = "0 0";
|
|
1225
|
-
this._rotatePane.style.transform = transform;
|
|
1226
1289
|
const matrix = this._pitchTransform = new DOMMatrix(transform), a = matrix.m11, b = matrix.m21, c = matrix.m41, d = matrix.m12, e = matrix.m22, f = matrix.m42, g = matrix.m14, h = matrix.m24, i = matrix.m44, determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
|
|
1290
|
+
const ground = DOMMatrix.fromMatrix(matrix);
|
|
1291
|
+
ground.m13 = ground.m23 = ground.m43 = 0;
|
|
1292
|
+
this._rotatePane.style.transform = `matrix3d(${Array.from(ground.toFloat64Array(), (value) => value * 1e3).join(",")})`;
|
|
1227
1293
|
this._pitchInverseTransform = {
|
|
1228
1294
|
a: (e * i - f * h) / determinant,
|
|
1229
1295
|
b: (c * h - b * i) / determinant,
|
|
@@ -1306,7 +1372,7 @@ var leafletRotate = (() => {
|
|
|
1306
1372
|
},
|
|
1307
1373
|
/**
|
|
1308
1374
|
* Fill the area above the far plane (where no tiles are loaded) with a sky gradient.
|
|
1309
|
-
* Style it through
|
|
1375
|
+
* Style it through `.leaflet-pitch-sky` or the `--leaflet-sky-background` CSS property.
|
|
1310
1376
|
*
|
|
1311
1377
|
* @since leaflet-rotate (0.5.1)
|
|
1312
1378
|
*/
|
|
@@ -1327,7 +1393,7 @@ var leafletRotate = (() => {
|
|
|
1327
1393
|
width: "100%",
|
|
1328
1394
|
zIndex: 0,
|
|
1329
1395
|
pointerEvents: "none",
|
|
1330
|
-
background: "linear-gradient(#9ec5e8, #dfe9f0)"
|
|
1396
|
+
background: "var(--leaflet-sky-background, linear-gradient(#9ec5e8, #dfe9f0))"
|
|
1331
1397
|
});
|
|
1332
1398
|
}
|
|
1333
1399
|
this._pitchSky.style.display = "";
|
|
@@ -1485,6 +1551,7 @@ var leafletRotate = (() => {
|
|
|
1485
1551
|
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
|
|
1486
1552
|
if (this._rotate) {
|
|
1487
1553
|
this._rotatePane = this.createPane("rotatePane", this._mapPane);
|
|
1554
|
+
this._mapPane.style.transformStyle = this._rotatePane.style.transformStyle = "preserve-3d";
|
|
1488
1555
|
this._norotatePane = this.createPane("norotatePane", this._mapPane);
|
|
1489
1556
|
if (L.version.startsWith("2.")) {
|
|
1490
1557
|
this._rotatePane.style.transformOrigin = "0 0";
|
|
@@ -1524,7 +1591,7 @@ var leafletRotate = (() => {
|
|
|
1524
1591
|
* @since leaflet-rotate (v0.1.4)
|
|
1525
1592
|
*/
|
|
1526
1593
|
panInside(latlng, options) {
|
|
1527
|
-
if (!this._rotate
|
|
1594
|
+
if (!this._rotate) {
|
|
1528
1595
|
return mapProto.panInside.apply(this, arguments);
|
|
1529
1596
|
}
|
|
1530
1597
|
options = options || {};
|
|
@@ -1541,53 +1608,87 @@ var leafletRotate = (() => {
|
|
|
1541
1608
|
return this;
|
|
1542
1609
|
},
|
|
1543
1610
|
/**
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1611
|
+
* Constrain the visible ground footprint, including its asymmetric pitched far side.
|
|
1612
|
+
* When it is larger than maxBounds, Leaflet's rebound centers the excess on each axis.
|
|
1613
|
+
*/
|
|
1614
|
+
_limitCenter(center, zoom, bounds) {
|
|
1615
|
+
if (!this._rotate || !bounds) {
|
|
1616
|
+
return mapProto._limitCenter.apply(this, arguments);
|
|
1617
|
+
}
|
|
1618
|
+
const centerPoint = this.project(center, zoom), layerCenter = this.containerPointToLayerPoint(this.getSize().divideBy(2)), footprint = this._getPaddedPixelBounds(0, false), viewBounds = new L.Bounds(
|
|
1619
|
+
centerPoint.add(footprint.min.subtract(layerCenter)),
|
|
1620
|
+
centerPoint.add(footprint.max.subtract(layerCenter))
|
|
1621
|
+
), offset = this._getBoundsOffset(viewBounds, bounds, zoom);
|
|
1622
|
+
if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
|
|
1623
|
+
return center;
|
|
1624
|
+
}
|
|
1625
|
+
return this.unproject(centerPoint.add(offset), zoom);
|
|
1626
|
+
},
|
|
1627
|
+
/**
|
|
1628
|
+
* Reapply configured maximum bounds after a map transform.
|
|
1550
1629
|
*
|
|
1551
|
-
* @
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1630
|
+
* @since leaflet-rotate (0.5.3)
|
|
1631
|
+
*/
|
|
1632
|
+
_panInsideMaxBounds() {
|
|
1633
|
+
if (this.options.maxBounds) {
|
|
1634
|
+
return mapProto._panInsideMaxBounds.apply(this, arguments);
|
|
1635
|
+
}
|
|
1636
|
+
},
|
|
1637
|
+
/**
|
|
1638
|
+
* Keep the visible ground footprint inside the given bounds.
|
|
1639
|
+
*
|
|
1640
|
+
* @param {L.LatLngBounds|Array} bounds Geographic bounds.
|
|
1641
|
+
* @param {Object} [options] Pan options passed to Leaflet.
|
|
1556
1642
|
* @returns {L.Map} This map instance.
|
|
1557
|
-
*
|
|
1558
|
-
* @since leaflet-rotate (
|
|
1643
|
+
*
|
|
1644
|
+
* @since leaflet-rotate (0.5.3)
|
|
1559
1645
|
*/
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1646
|
+
panInsideBounds(bounds, options) {
|
|
1647
|
+
if (this._rotate) {
|
|
1648
|
+
bounds = bounds instanceof L.LatLngBounds ? bounds : new L.LatLngBounds(bounds);
|
|
1649
|
+
const center = this.getCenter(), limited = this._limitCenter(center, this._zoom, bounds);
|
|
1650
|
+
const offset = this._getCenterOffset(limited);
|
|
1651
|
+
if (!center.equals(limited) && Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
|
|
1652
|
+
this._enforcingBounds = true;
|
|
1653
|
+
this.setView(limited, this._zoom, { reset: true });
|
|
1654
|
+
this._enforcingBounds = false;
|
|
1655
|
+
return this;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
return mapProto.panInsideBounds.apply(this, arguments);
|
|
1659
|
+
},
|
|
1660
|
+
/**
|
|
1661
|
+
* Leaflet supplies inertia offsets in screen pixels, not projected CRS pixels.
|
|
1662
|
+
* Keep the drag's moving anchor when inertia continues a pitched pan.
|
|
1663
|
+
*/
|
|
1664
|
+
_limitOffset(offset, bounds) {
|
|
1665
|
+
if (!this._rotate || !bounds) {
|
|
1666
|
+
return mapProto._limitOffset.apply(this, arguments);
|
|
1667
|
+
}
|
|
1668
|
+
const pan = this._pitchPan, anchor = pan ? pan.anchor.add(this._getMapPanePos().subtract(pan.panePos)) : this.getSize().divideBy(2), layerAnchor = this.containerPointToLayerPoint(anchor), panePos = this._getMapPanePos(), target = anchor.subtract(offset).subtract(panePos), shift = (this._pitch ? this._transformPitchPoint(target, this._pitchInverseTransform, anchor.subtract(panePos)) : this.containerPointToLayerPoint(anchor.subtract(offset))).subtract(layerAnchor), footprint = this._getPaddedPixelBounds(0, false), origin = this.getPixelOrigin().subtract(shift), correction = this._getBoundsOffset(new L.Bounds(
|
|
1669
|
+
footprint.min.add(origin),
|
|
1670
|
+
footprint.max.add(origin)
|
|
1671
|
+
), bounds);
|
|
1672
|
+
return anchor.subtract(this.layerPointToContainerPoint(layerAnchor.add(shift).subtract(correction)));
|
|
1673
|
+
},
|
|
1674
|
+
/**
|
|
1675
|
+
* Pan by a screen-pixel offset, constraining large immediate pans to maxBounds.
|
|
1676
|
+
*
|
|
1677
|
+
* @param {L.Point|number[]} offset Pan offset in pixels.
|
|
1678
|
+
* @param {Object} [options] Pan options passed to Leaflet.
|
|
1679
|
+
* @returns {L.Map} This map instance.
|
|
1680
|
+
*
|
|
1681
|
+
* @since leaflet-rotate (0.5.3)
|
|
1682
|
+
*/
|
|
1683
|
+
panBy(offset, options) {
|
|
1684
|
+
offset = toPoint(offset);
|
|
1685
|
+
if (this._rotate && this._loaded && !this.getSize().contains(offset) && !(options && options.animate)) {
|
|
1686
|
+
const anchor = this.getSize().divideBy(2), panePos = this._getMapPanePos(), layerAnchor = this.containerPointToLayerPoint(anchor), target = anchor.subtract(offset).subtract(panePos), shift = (this._pitch ? this._transformPitchPoint(target, this._pitchInverseTransform, anchor.subtract(panePos)) : this.containerPointToLayerPoint(anchor.subtract(offset))).subtract(layerAnchor), center = this.unproject(this.project(this.getCenter()).subtract(shift));
|
|
1687
|
+
this._resetView(this._limitCenter(center, this._zoom, this.options.maxBounds), this._zoom);
|
|
1688
|
+
return this;
|
|
1689
|
+
}
|
|
1690
|
+
return mapProto.panBy.call(this, offset, options);
|
|
1691
|
+
},
|
|
1591
1692
|
// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
|
|
1592
1693
|
// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
|
|
1593
1694
|
// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
|
|
@@ -1671,7 +1772,7 @@ var leafletRotate = (() => {
|
|
|
1671
1772
|
const center = this.getSize().divideBy(2), layerCenter = this.containerPointToLayerPoint(center), opposite = layerCenter.multiplyBy(2).subtract(this.project(latlng).subtract(this._pixelOrigin));
|
|
1672
1773
|
return center.subtract(this.layerPointToContainerPoint(opposite));
|
|
1673
1774
|
}
|
|
1674
|
-
return this.
|
|
1775
|
+
return this.layerPointToContainerPoint(this.project(latlng).subtract(this._pixelOrigin))._subtract(this.getSize()._divideBy(2));
|
|
1675
1776
|
},
|
|
1676
1777
|
/**
|
|
1677
1778
|
* Return the current position of the rotated pane.
|
|
@@ -1701,7 +1802,7 @@ var leafletRotate = (() => {
|
|
|
1701
1802
|
if (!this._rotate) {
|
|
1702
1803
|
return mapProto._getNewPixelOrigin.apply(this, arguments);
|
|
1703
1804
|
}
|
|
1704
|
-
return this.project(center, zoom)._subtract(this.containerPointToLayerPoint(this.getSize()._divideBy(2)))
|
|
1805
|
+
return this.project(center, zoom)._subtract(this.containerPointToLayerPoint(this.getSize()._divideBy(2)));
|
|
1705
1806
|
},
|
|
1706
1807
|
/**
|
|
1707
1808
|
* Calculate the pixel bounds needed to cover the transformed viewport.
|
|
@@ -1750,22 +1851,26 @@ var leafletRotate = (() => {
|
|
|
1750
1851
|
* @see src\layer\vector\Renderer::_update()
|
|
1751
1852
|
*
|
|
1752
1853
|
* @param {number} padding Bounds padding as a fraction of map size.
|
|
1854
|
+
* @param {boolean} [round=true] Round renderer bounds; navigation needs exact ground coordinates.
|
|
1753
1855
|
*
|
|
1754
1856
|
* @returns {L.Bounds} Padded bounds in layer-point coordinates.
|
|
1755
1857
|
*
|
|
1756
1858
|
* @since leaflet-rotate (v0.2.0)
|
|
1757
1859
|
*/
|
|
1758
|
-
_getPaddedPixelBounds(padding) {
|
|
1860
|
+
_getPaddedPixelBounds(padding, round = true) {
|
|
1759
1861
|
if (!this._rotate && mapProto._getPaddedPixelBounds) {
|
|
1760
1862
|
return mapProto._getPaddedPixelBounds.apply(this, arguments);
|
|
1761
1863
|
}
|
|
1762
1864
|
const [padMin, padMax] = this._getPitchedContainerBounds(padding);
|
|
1763
1865
|
return new L.Bounds([
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
])
|
|
1866
|
+
[padMin.x, padMin.y],
|
|
1867
|
+
[padMin.x, padMax.y],
|
|
1868
|
+
[padMax.x, padMin.y],
|
|
1869
|
+
[padMax.x, padMax.y]
|
|
1870
|
+
].map((point) => {
|
|
1871
|
+
const projected = this.containerPointToLayerPoint(point);
|
|
1872
|
+
return round ? projected.floor() : projected;
|
|
1873
|
+
}));
|
|
1769
1874
|
},
|
|
1770
1875
|
/**
|
|
1771
1876
|
* Build and fire Leaflet's location-found event, including the device heading.
|
|
@@ -1946,6 +2051,55 @@ var leafletRotate = (() => {
|
|
|
1946
2051
|
});
|
|
1947
2052
|
L.Map.addInitHook("addHandler", "trackContainerMutation", L.Map.ContainerMutation);
|
|
1948
2053
|
|
|
2054
|
+
// src/map/handler/Drag.js
|
|
2055
|
+
var dragProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Map.Drag.prototype));
|
|
2056
|
+
L.Map.Drag.include({
|
|
2057
|
+
/**
|
|
2058
|
+
* Capture the ground footprint and pointer anchor before a constrained drag.
|
|
2059
|
+
*/
|
|
2060
|
+
_onDragStart() {
|
|
2061
|
+
dragProto._onDragStart.apply(this, arguments);
|
|
2062
|
+
const map = this._map;
|
|
2063
|
+
this._rotateLimit = null;
|
|
2064
|
+
if (!map._rotate || !map.options.maxBounds || !map.options.maxBoundsViscosity) {
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
const anchor = (map.pointerEventToContainerPoint || map.mouseEventToContainerPoint).call(map, {
|
|
2068
|
+
clientX: this._draggable._startPoint.x,
|
|
2069
|
+
clientY: this._draggable._startPoint.y
|
|
2070
|
+
}), footprint = map._getPaddedPixelBounds(0, false), origin = map.getPixelOrigin();
|
|
2071
|
+
this._rotateLimit = {
|
|
2072
|
+
anchor,
|
|
2073
|
+
layerAnchor: map.containerPointToLayerPoint(anchor),
|
|
2074
|
+
panePos: map._getMapPanePos().clone(),
|
|
2075
|
+
footprint: new L.Bounds(footprint.min.add(origin), footprint.max.add(origin)),
|
|
2076
|
+
matrix: map._pitch ? map._pitchTransform : null,
|
|
2077
|
+
inverse: map._pitchInverseTransform,
|
|
2078
|
+
bearing: map._bearing
|
|
2079
|
+
};
|
|
2080
|
+
},
|
|
2081
|
+
/**
|
|
2082
|
+
* Apply max-bounds viscosity in ground coordinates during a rotated drag.
|
|
2083
|
+
*/
|
|
2084
|
+
_onPreDragLimit() {
|
|
2085
|
+
const limit = this._rotateLimit;
|
|
2086
|
+
if (!this._map._rotate || !limit) {
|
|
2087
|
+
return dragProto._onPreDragLimit.apply(this, arguments);
|
|
2088
|
+
}
|
|
2089
|
+
const map = this._map, draggable = this._draggable, offset = draggable._newPos.subtract(draggable._startPos), shift = limit.matrix ? map._transformPitchPoint(
|
|
2090
|
+
limit.anchor.add(offset).subtract(limit.panePos),
|
|
2091
|
+
limit.inverse,
|
|
2092
|
+
limit.anchor.subtract(limit.panePos)
|
|
2093
|
+
).subtract(limit.layerAnchor) : offset.rotate(-limit.bearing), correction = map._getBoundsOffset(new L.Bounds(
|
|
2094
|
+
limit.footprint.min.subtract(shift),
|
|
2095
|
+
limit.footprint.max.subtract(shift)
|
|
2096
|
+
), map.options.maxBounds);
|
|
2097
|
+
shift._subtract(correction.multiplyBy(this._viscosity));
|
|
2098
|
+
const limitedOffset = limit.matrix ? map._transformPitchPoint(limit.layerAnchor.add(shift), limit.matrix).add(limit.panePos).subtract(limit.anchor) : shift.rotate(limit.bearing);
|
|
2099
|
+
draggable._newPos = draggable._startPos.add(limitedOffset);
|
|
2100
|
+
}
|
|
2101
|
+
});
|
|
2102
|
+
|
|
1949
2103
|
// src/map/handler/TouchGestures.js
|
|
1950
2104
|
function touchToContainerPoint(map, touch) {
|
|
1951
2105
|
const container = map.getContainer(), rect = container.getBoundingClientRect();
|