leaflet-rotate 0.5.2 → 0.5.3
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 +208 -68
- 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/package.json +1 -1
- 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 +148 -56
- package/src/map/handler/Drag.js +59 -0
- package/test/features/navigation-bounds.feature +33 -0
- package/test/features/overlays.feature +9 -0
- package/test/features/pitch.feature +19 -0
- package/test/index.html +70 -6
- package/test/steps/navigation-bounds.steps.js +195 -0
- package/test/steps/overlays.steps.js +44 -0
- package/test/steps/pitch.steps.js +264 -0
- package/test/steps/rotation.steps.js +28 -4
- package/test/support/hooks.js +19 -7
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.3/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.3/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);
|
|
@@ -478,6 +478,24 @@ var leafletRotate = (() => {
|
|
|
478
478
|
if (this._map._rotate) {
|
|
479
479
|
pos = this._map.rotatedPointToMapPanePoint(pos);
|
|
480
480
|
}
|
|
481
|
+
const bearing = this._getIconBearing();
|
|
482
|
+
if (this._icon) {
|
|
483
|
+
L.DomUtil.setPosition(this._icon, pos, bearing, pos, this.options.scale);
|
|
484
|
+
}
|
|
485
|
+
if (this._shadow) {
|
|
486
|
+
L.DomUtil.setPosition(this._shadow, pos, bearing, pos, this.options.scale);
|
|
487
|
+
}
|
|
488
|
+
this._zIndex = pos.y + this.options.zIndexOffset;
|
|
489
|
+
this._resetZIndex();
|
|
490
|
+
},
|
|
491
|
+
/**
|
|
492
|
+
* Compute the on-screen icon rotation from `rotation`, `rotateWithView` and `keepUpright`.
|
|
493
|
+
*
|
|
494
|
+
* @returns {number} Angle in radians.
|
|
495
|
+
*
|
|
496
|
+
* @since leaflet-rotate (0.5.3)
|
|
497
|
+
*/
|
|
498
|
+
_getIconBearing() {
|
|
481
499
|
let bearing = this.options.rotation || 0;
|
|
482
500
|
if (this.options.rotateWithView) {
|
|
483
501
|
bearing += this._map._bearing;
|
|
@@ -489,14 +507,7 @@ var leafletRotate = (() => {
|
|
|
489
507
|
}
|
|
490
508
|
bearing %= 2 * Math.PI;
|
|
491
509
|
}
|
|
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();
|
|
510
|
+
return bearing;
|
|
500
511
|
},
|
|
501
512
|
// _updateZIndex(offset) {
|
|
502
513
|
// if (!this._map._rotate) {
|
|
@@ -541,9 +552,24 @@ var leafletRotate = (() => {
|
|
|
541
552
|
}
|
|
542
553
|
return events;
|
|
543
554
|
},
|
|
555
|
+
/**
|
|
556
|
+
* Preserve the 3D context on tile containers without making individual tile levels 3D.
|
|
557
|
+
*
|
|
558
|
+
* @since leaflet-rotate (0.5.3)
|
|
559
|
+
*/
|
|
560
|
+
_initContainer() {
|
|
561
|
+
gridLayerProto._initContainer.apply(this, arguments);
|
|
562
|
+
if (!this._map._rotate || !this._container) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
for (let pane = this._container; pane && pane !== this._map._rotatePane; pane = pane.parentElement) {
|
|
566
|
+
pane.style.transformStyle = "preserve-3d";
|
|
567
|
+
}
|
|
568
|
+
},
|
|
544
569
|
/**
|
|
545
570
|
* Keep tile levels aligned when a bearing/pitch change shifts the pixel origin
|
|
546
571
|
* (eg. when the rotation pivot is not the view center), then refresh tiles (throttled).
|
|
572
|
+
* The first event also synchronizes levels created before any accumulated pans.
|
|
547
573
|
*
|
|
548
574
|
* @listens L.Map~rotate
|
|
549
575
|
*
|
|
@@ -555,7 +581,7 @@ var leafletRotate = (() => {
|
|
|
555
581
|
return;
|
|
556
582
|
}
|
|
557
583
|
const origin = map.getPixelOrigin();
|
|
558
|
-
if (this._rotateOrigin
|
|
584
|
+
if ((!this._rotateOrigin || !this._rotateOrigin.equals(origin)) && !map._animatingZoom && this._levels) {
|
|
559
585
|
this._setZoomTransforms(map.getCenter(), map.getZoom());
|
|
560
586
|
}
|
|
561
587
|
this._rotateOrigin = origin;
|
|
@@ -796,6 +822,19 @@ var leafletRotate = (() => {
|
|
|
796
822
|
pitchMaxDistance: 3
|
|
797
823
|
});
|
|
798
824
|
L.Map.include({
|
|
825
|
+
/**
|
|
826
|
+
* Rebuild perspective before Leaflet calculates the origin of a reset view.
|
|
827
|
+
* A reset clears the outer pane translation, unlike a completed pan.
|
|
828
|
+
*/
|
|
829
|
+
_resetView() {
|
|
830
|
+
if (this._pitch) {
|
|
831
|
+
delete this._pitchPan;
|
|
832
|
+
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
|
|
833
|
+
this._updatePitchTransform();
|
|
834
|
+
arguments[0] = this._limitCenter(arguments[0], arguments[1], this.options.maxBounds);
|
|
835
|
+
}
|
|
836
|
+
return mapProto._resetView.apply(this, arguments);
|
|
837
|
+
},
|
|
799
838
|
/**
|
|
800
839
|
* Apply an immediate animated-zoom frame without waiting for a transition delay.
|
|
801
840
|
*
|
|
@@ -835,6 +874,7 @@ var leafletRotate = (() => {
|
|
|
835
874
|
this.on("move", this._updatePitchPan, this);
|
|
836
875
|
this.on("moveend", this._endPitchPan, this);
|
|
837
876
|
this.on("move", this._updatePitchSky, this);
|
|
877
|
+
this.on("rotate", this._panInsideMaxBounds, this);
|
|
838
878
|
this.setBearing(this.options.bearing);
|
|
839
879
|
this.setPitch(this.options.pitch);
|
|
840
880
|
}
|
|
@@ -1180,22 +1220,32 @@ var leafletRotate = (() => {
|
|
|
1180
1220
|
* @param {L.Point|number[]} point Input pixel coordinate.
|
|
1181
1221
|
* @param {DOMMatrix|Object} matrix Forward CSS matrix or inverse homography with
|
|
1182
1222
|
* row-major coefficients `a` through `i`.
|
|
1223
|
+
* @param {L.Point} [anchor] Keep inverse pan coordinates on this anchor's side of the horizon.
|
|
1183
1224
|
*
|
|
1184
1225
|
* @returns {L.Point} Transformed pixel coordinate.
|
|
1185
1226
|
*
|
|
1186
1227
|
* @since leaflet-rotate (0.5.0)
|
|
1187
1228
|
*/
|
|
1188
|
-
_transformPitchPoint(point, matrix) {
|
|
1229
|
+
_transformPitchPoint(point, matrix, anchor) {
|
|
1189
1230
|
point = toPoint(point);
|
|
1190
1231
|
if (!(matrix instanceof DOMMatrix)) {
|
|
1191
|
-
|
|
1232
|
+
if (anchor) {
|
|
1233
|
+
const anchorW = matrix.g * anchor.x + matrix.h * anchor.y + matrix.i, pointW = matrix.g * point.x + matrix.h * point.y + matrix.i;
|
|
1234
|
+
if (pointW * anchorW <= 0) {
|
|
1235
|
+
point = anchor.add(point.subtract(anchor).multiplyBy(anchorW / (anchorW - pointW) * (1 - 1e-6)));
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
const denominator2 = matrix.g * point.x + matrix.h * point.y + matrix.i;
|
|
1192
1239
|
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) /
|
|
1240
|
+
(matrix.a * point.x + matrix.b * point.y + matrix.c) / denominator2,
|
|
1241
|
+
(matrix.d * point.x + matrix.e * point.y + matrix.f) / denominator2
|
|
1195
1242
|
);
|
|
1196
1243
|
}
|
|
1197
|
-
const
|
|
1198
|
-
return new L.Point(
|
|
1244
|
+
const denominator = matrix.m14 * point.x + matrix.m24 * point.y + matrix.m44;
|
|
1245
|
+
return new L.Point(
|
|
1246
|
+
(matrix.m11 * point.x + matrix.m21 * point.y + matrix.m41) / denominator,
|
|
1247
|
+
(matrix.m12 * point.x + matrix.m22 * point.y + matrix.m42) / denominator
|
|
1248
|
+
);
|
|
1199
1249
|
},
|
|
1200
1250
|
/**
|
|
1201
1251
|
* Apply the current pitch/bearing CSS transform and cache its forward and inverse matrices.
|
|
@@ -1222,8 +1272,10 @@ var leafletRotate = (() => {
|
|
|
1222
1272
|
*/
|
|
1223
1273
|
_setPitchTransform(transform) {
|
|
1224
1274
|
this._rotatePane.style.transformOrigin = "0 0";
|
|
1225
|
-
this._rotatePane.style.transform = transform;
|
|
1226
1275
|
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);
|
|
1276
|
+
const ground = DOMMatrix.fromMatrix(matrix);
|
|
1277
|
+
ground.m13 = ground.m23 = ground.m43 = 0;
|
|
1278
|
+
this._rotatePane.style.transform = `matrix3d(${Array.from(ground.toFloat64Array(), (value) => value * 1e3).join(",")})`;
|
|
1227
1279
|
this._pitchInverseTransform = {
|
|
1228
1280
|
a: (e * i - f * h) / determinant,
|
|
1229
1281
|
b: (c * h - b * i) / determinant,
|
|
@@ -1306,7 +1358,7 @@ var leafletRotate = (() => {
|
|
|
1306
1358
|
},
|
|
1307
1359
|
/**
|
|
1308
1360
|
* Fill the area above the far plane (where no tiles are loaded) with a sky gradient.
|
|
1309
|
-
* Style it through
|
|
1361
|
+
* Style it through `.leaflet-pitch-sky` or the `--leaflet-sky-background` CSS property.
|
|
1310
1362
|
*
|
|
1311
1363
|
* @since leaflet-rotate (0.5.1)
|
|
1312
1364
|
*/
|
|
@@ -1327,7 +1379,7 @@ var leafletRotate = (() => {
|
|
|
1327
1379
|
width: "100%",
|
|
1328
1380
|
zIndex: 0,
|
|
1329
1381
|
pointerEvents: "none",
|
|
1330
|
-
background: "linear-gradient(#9ec5e8, #dfe9f0)"
|
|
1382
|
+
background: "var(--leaflet-sky-background, linear-gradient(#9ec5e8, #dfe9f0))"
|
|
1331
1383
|
});
|
|
1332
1384
|
}
|
|
1333
1385
|
this._pitchSky.style.display = "";
|
|
@@ -1485,6 +1537,7 @@ var leafletRotate = (() => {
|
|
|
1485
1537
|
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
|
|
1486
1538
|
if (this._rotate) {
|
|
1487
1539
|
this._rotatePane = this.createPane("rotatePane", this._mapPane);
|
|
1540
|
+
this._mapPane.style.transformStyle = this._rotatePane.style.transformStyle = "preserve-3d";
|
|
1488
1541
|
this._norotatePane = this.createPane("norotatePane", this._mapPane);
|
|
1489
1542
|
if (L.version.startsWith("2.")) {
|
|
1490
1543
|
this._rotatePane.style.transformOrigin = "0 0";
|
|
@@ -1541,53 +1594,87 @@ var leafletRotate = (() => {
|
|
|
1541
1594
|
return this;
|
|
1542
1595
|
},
|
|
1543
1596
|
/**
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1597
|
+
* Constrain the visible ground footprint, including its asymmetric pitched far side.
|
|
1598
|
+
* When it is larger than maxBounds, Leaflet's rebound centers the excess on each axis.
|
|
1599
|
+
*/
|
|
1600
|
+
_limitCenter(center, zoom, bounds) {
|
|
1601
|
+
if (!this._rotate || !bounds) {
|
|
1602
|
+
return mapProto._limitCenter.apply(this, arguments);
|
|
1603
|
+
}
|
|
1604
|
+
const centerPoint = this.project(center, zoom), layerCenter = this.containerPointToLayerPoint(this.getSize().divideBy(2)), footprint = this._getPaddedPixelBounds(0, false), viewBounds = new L.Bounds(
|
|
1605
|
+
centerPoint.add(footprint.min.subtract(layerCenter)),
|
|
1606
|
+
centerPoint.add(footprint.max.subtract(layerCenter))
|
|
1607
|
+
), offset = this._getBoundsOffset(viewBounds, bounds, zoom);
|
|
1608
|
+
if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
|
|
1609
|
+
return center;
|
|
1610
|
+
}
|
|
1611
|
+
return this.unproject(centerPoint.add(offset), zoom);
|
|
1612
|
+
},
|
|
1613
|
+
/**
|
|
1614
|
+
* Reapply configured maximum bounds after a map transform.
|
|
1550
1615
|
*
|
|
1551
|
-
* @
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1616
|
+
* @since leaflet-rotate (0.5.3)
|
|
1617
|
+
*/
|
|
1618
|
+
_panInsideMaxBounds() {
|
|
1619
|
+
if (this.options.maxBounds) {
|
|
1620
|
+
return mapProto._panInsideMaxBounds.apply(this, arguments);
|
|
1621
|
+
}
|
|
1622
|
+
},
|
|
1623
|
+
/**
|
|
1624
|
+
* Keep the visible ground footprint inside the given bounds.
|
|
1625
|
+
*
|
|
1626
|
+
* @param {L.LatLngBounds|Array} bounds Geographic bounds.
|
|
1627
|
+
* @param {Object} [options] Pan options passed to Leaflet.
|
|
1556
1628
|
* @returns {L.Map} This map instance.
|
|
1557
|
-
*
|
|
1558
|
-
* @since leaflet-rotate (
|
|
1629
|
+
*
|
|
1630
|
+
* @since leaflet-rotate (0.5.3)
|
|
1559
1631
|
*/
|
|
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
|
-
|
|
1632
|
+
panInsideBounds(bounds, options) {
|
|
1633
|
+
if (this._rotate) {
|
|
1634
|
+
bounds = bounds instanceof L.LatLngBounds ? bounds : new L.LatLngBounds(bounds);
|
|
1635
|
+
const center = this.getCenter(), limited = this._limitCenter(center, this._zoom, bounds);
|
|
1636
|
+
const offset = this._getCenterOffset(limited);
|
|
1637
|
+
if (!center.equals(limited) && Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
|
|
1638
|
+
this._enforcingBounds = true;
|
|
1639
|
+
this.setView(limited, this._zoom, { reset: true });
|
|
1640
|
+
this._enforcingBounds = false;
|
|
1641
|
+
return this;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
return mapProto.panInsideBounds.apply(this, arguments);
|
|
1645
|
+
},
|
|
1646
|
+
/**
|
|
1647
|
+
* Leaflet supplies inertia offsets in screen pixels, not projected CRS pixels.
|
|
1648
|
+
* Keep the drag's moving anchor when inertia continues a pitched pan.
|
|
1649
|
+
*/
|
|
1650
|
+
_limitOffset(offset, bounds) {
|
|
1651
|
+
if (!this._rotate || !bounds) {
|
|
1652
|
+
return mapProto._limitOffset.apply(this, arguments);
|
|
1653
|
+
}
|
|
1654
|
+
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(
|
|
1655
|
+
footprint.min.add(origin),
|
|
1656
|
+
footprint.max.add(origin)
|
|
1657
|
+
), bounds);
|
|
1658
|
+
return anchor.subtract(this.layerPointToContainerPoint(layerAnchor.add(shift).subtract(correction)));
|
|
1659
|
+
},
|
|
1660
|
+
/**
|
|
1661
|
+
* Pan by a screen-pixel offset, constraining large immediate pans to maxBounds.
|
|
1662
|
+
*
|
|
1663
|
+
* @param {L.Point|number[]} offset Pan offset in pixels.
|
|
1664
|
+
* @param {Object} [options] Pan options passed to Leaflet.
|
|
1665
|
+
* @returns {L.Map} This map instance.
|
|
1666
|
+
*
|
|
1667
|
+
* @since leaflet-rotate (0.5.3)
|
|
1668
|
+
*/
|
|
1669
|
+
panBy(offset, options) {
|
|
1670
|
+
offset = toPoint(offset);
|
|
1671
|
+
if (this._rotate && this._loaded && !this.getSize().contains(offset) && !(options && options.animate)) {
|
|
1672
|
+
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));
|
|
1673
|
+
this._resetView(this._limitCenter(center, this._zoom, this.options.maxBounds), this._zoom);
|
|
1674
|
+
return this;
|
|
1675
|
+
}
|
|
1676
|
+
return mapProto.panBy.call(this, offset, options);
|
|
1677
|
+
},
|
|
1591
1678
|
// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
|
|
1592
1679
|
// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
|
|
1593
1680
|
// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
|
|
@@ -1750,22 +1837,26 @@ var leafletRotate = (() => {
|
|
|
1750
1837
|
* @see src\layer\vector\Renderer::_update()
|
|
1751
1838
|
*
|
|
1752
1839
|
* @param {number} padding Bounds padding as a fraction of map size.
|
|
1840
|
+
* @param {boolean} [round=true] Round renderer bounds; navigation needs exact ground coordinates.
|
|
1753
1841
|
*
|
|
1754
1842
|
* @returns {L.Bounds} Padded bounds in layer-point coordinates.
|
|
1755
1843
|
*
|
|
1756
1844
|
* @since leaflet-rotate (v0.2.0)
|
|
1757
1845
|
*/
|
|
1758
|
-
_getPaddedPixelBounds(padding) {
|
|
1846
|
+
_getPaddedPixelBounds(padding, round = true) {
|
|
1759
1847
|
if (!this._rotate && mapProto._getPaddedPixelBounds) {
|
|
1760
1848
|
return mapProto._getPaddedPixelBounds.apply(this, arguments);
|
|
1761
1849
|
}
|
|
1762
1850
|
const [padMin, padMax] = this._getPitchedContainerBounds(padding);
|
|
1763
1851
|
return new L.Bounds([
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
])
|
|
1852
|
+
[padMin.x, padMin.y],
|
|
1853
|
+
[padMin.x, padMax.y],
|
|
1854
|
+
[padMax.x, padMin.y],
|
|
1855
|
+
[padMax.x, padMax.y]
|
|
1856
|
+
].map((point) => {
|
|
1857
|
+
const projected = this.containerPointToLayerPoint(point);
|
|
1858
|
+
return round ? projected.floor() : projected;
|
|
1859
|
+
}));
|
|
1769
1860
|
},
|
|
1770
1861
|
/**
|
|
1771
1862
|
* Build and fire Leaflet's location-found event, including the device heading.
|
|
@@ -1946,6 +2037,55 @@ var leafletRotate = (() => {
|
|
|
1946
2037
|
});
|
|
1947
2038
|
L.Map.addInitHook("addHandler", "trackContainerMutation", L.Map.ContainerMutation);
|
|
1948
2039
|
|
|
2040
|
+
// src/map/handler/Drag.js
|
|
2041
|
+
var dragProto = Object.defineProperties({}, Object.getOwnPropertyDescriptors(L.Map.Drag.prototype));
|
|
2042
|
+
L.Map.Drag.include({
|
|
2043
|
+
/**
|
|
2044
|
+
* Capture the ground footprint and pointer anchor before a constrained drag.
|
|
2045
|
+
*/
|
|
2046
|
+
_onDragStart() {
|
|
2047
|
+
dragProto._onDragStart.apply(this, arguments);
|
|
2048
|
+
const map = this._map;
|
|
2049
|
+
this._rotateLimit = null;
|
|
2050
|
+
if (!map._rotate || !map.options.maxBounds || !map.options.maxBoundsViscosity) {
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
const anchor = (map.pointerEventToContainerPoint || map.mouseEventToContainerPoint).call(map, {
|
|
2054
|
+
clientX: this._draggable._startPoint.x,
|
|
2055
|
+
clientY: this._draggable._startPoint.y
|
|
2056
|
+
}), footprint = map._getPaddedPixelBounds(0, false), origin = map.getPixelOrigin();
|
|
2057
|
+
this._rotateLimit = {
|
|
2058
|
+
anchor,
|
|
2059
|
+
layerAnchor: map.containerPointToLayerPoint(anchor),
|
|
2060
|
+
panePos: map._getMapPanePos().clone(),
|
|
2061
|
+
footprint: new L.Bounds(footprint.min.add(origin), footprint.max.add(origin)),
|
|
2062
|
+
matrix: map._pitch ? map._pitchTransform : null,
|
|
2063
|
+
inverse: map._pitchInverseTransform,
|
|
2064
|
+
bearing: map._bearing
|
|
2065
|
+
};
|
|
2066
|
+
},
|
|
2067
|
+
/**
|
|
2068
|
+
* Apply max-bounds viscosity in ground coordinates during a rotated drag.
|
|
2069
|
+
*/
|
|
2070
|
+
_onPreDragLimit() {
|
|
2071
|
+
const limit = this._rotateLimit;
|
|
2072
|
+
if (!this._map._rotate || !limit) {
|
|
2073
|
+
return dragProto._onPreDragLimit.apply(this, arguments);
|
|
2074
|
+
}
|
|
2075
|
+
const map = this._map, draggable = this._draggable, offset = draggable._newPos.subtract(draggable._startPos), shift = limit.matrix ? map._transformPitchPoint(
|
|
2076
|
+
limit.anchor.add(offset).subtract(limit.panePos),
|
|
2077
|
+
limit.inverse,
|
|
2078
|
+
limit.anchor.subtract(limit.panePos)
|
|
2079
|
+
).subtract(limit.layerAnchor) : offset.rotate(-limit.bearing), correction = map._getBoundsOffset(new L.Bounds(
|
|
2080
|
+
limit.footprint.min.subtract(shift),
|
|
2081
|
+
limit.footprint.max.subtract(shift)
|
|
2082
|
+
), map.options.maxBounds);
|
|
2083
|
+
shift._subtract(correction.multiplyBy(this._viscosity));
|
|
2084
|
+
const limitedOffset = limit.matrix ? map._transformPitchPoint(limit.layerAnchor.add(shift), limit.matrix).add(limit.panePos).subtract(limit.anchor) : shift.rotate(limit.bearing);
|
|
2085
|
+
draggable._newPos = draggable._startPos.add(limitedOffset);
|
|
2086
|
+
}
|
|
2087
|
+
});
|
|
2088
|
+
|
|
1949
2089
|
// src/map/handler/TouchGestures.js
|
|
1950
2090
|
function touchToContainerPoint(map, touch) {
|
|
1951
2091
|
const container = map.getContainer(), rect = container.getBoundingClientRect();
|