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 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.2/dist/leaflet-rotate.js"></script>
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.2/dist/leaflet-rotate.js"></script>
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
- response.writeHead(200, {
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
- if (this._icon) {
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 && !this._rotateOrigin.equals(origin) && !map._animatingZoom && this._levels) {
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
- const denominator = matrix.g * point.x + matrix.h * point.y + matrix.i;
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) / denominator,
1194
- (matrix.d * point.x + matrix.e * point.y + matrix.f) / denominator
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 transformed = new DOMPoint(point.x, point.y).matrixTransform(matrix);
1198
- return new L.Point(transformed.x / transformed.w, transformed.y / transformed.w);
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 the `.leaflet-pitch-sky` class.
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
- * Pans the map to the closest view that would lie inside the given bounds
1545
- * (if it's not already), controlling the animation using the options specific,
1546
- * if any.
1547
- *
1548
- * @TODO check if map bounds calculated by `L.Map::panInsideBounds()`
1549
- * function should match the `rotatePane` or `norotatePane` bounds
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
- * @see https://github.com/fnicollet/Leaflet/issues/7
1552
- *
1553
- * @param {L.LatLngBounds} bounds Geographic bounds to fit inside the viewport.
1554
- * @param {Object} [options] Pan animation options.
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 (v0.1.0)
1629
+ *
1630
+ * @since leaflet-rotate (0.5.3)
1559
1631
  */
1560
- // panInsideBounds(bounds, options) {
1561
- // this._enforcingBounds = true;
1562
- // const center = this.getCenter(),
1563
- // newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
1564
- //
1565
- // if (!center.equals(newCenter)) {
1566
- // this.panTo(newCenter, options);
1567
- // }
1568
- //
1569
- // this._enforcingBounds = false;
1570
- // return this;
1571
- // },
1572
- // adjust center for view to get inside bounds
1573
- // _limitCenter(center, zoom, bounds) {
1574
- //
1575
- // if (!bounds) { return center; }
1576
- //
1577
- // const centerPoint = this.project(center, zoom),
1578
- // viewHalf = this.getSize().divideBy(2),
1579
- // viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
1580
- // offset = this._getBoundsOffset(viewBounds, bounds, zoom);
1581
- //
1582
- // // If offset is less than a pixel, ignore.
1583
- // // This prevents unstable projections from getting into
1584
- // // an infinite loop of tiny offsets.
1585
- // if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
1586
- // return center;
1587
- // }
1588
- //
1589
- // return this.unproject(centerPoint.add(offset), zoom);
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
- this.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),
1765
- this.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),
1766
- this.containerPointToLayerPoint([padMax.x, padMin.y]).floor(),
1767
- this.containerPointToLayerPoint([padMax.x, padMax.y]).floor()
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();