leaflet-rotate 0.5.1 → 0.5.2

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.1/dist/leaflet-rotate.js"></script>
29
+ <script src="https://unpkg.com/leaflet-rotate@0.5.2/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.1/dist/leaflet-rotate.js"></script>
38
+ <script src="https://unpkg.com/leaflet-rotate@0.5.2/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:
@@ -831,6 +831,9 @@ var leafletRotate = (() => {
831
831
  mapProto.initialize.apply(this, arguments);
832
832
  if (this.options.rotate) {
833
833
  this.on("resize", this._updatePitchTransform, this);
834
+ this.on("movestart dragstart", this._startPitchPan, this);
835
+ this.on("move", this._updatePitchPan, this);
836
+ this.on("moveend", this._endPitchPan, this);
834
837
  this.on("move", this._updatePitchSky, this);
835
838
  this.setBearing(this.options.bearing);
836
839
  this.setPitch(this.options.pitch);
@@ -951,7 +954,8 @@ var leafletRotate = (() => {
951
954
  /**
952
955
  * Return the geographic bounds of the current viewport, accounting for rotation and pitch.
953
956
  *
954
- * The result is the smallest geographic bounds containing all four viewport corners.
957
+ * The result encloses the four corners of the visible ground rectangle. With pitch,
958
+ * the rectangle stops at the configured far plane, excluding the sky.
955
959
  *
956
960
  * @returns {L.LatLngBounds} Geographic bounds visible in the map viewport.
957
961
  *
@@ -1170,10 +1174,12 @@ var leafletRotate = (() => {
1170
1174
  this.fire("rotate");
1171
1175
  },
1172
1176
  /**
1173
- * Transform a point with a projective matrix, accepting DOMMatrix or its 2D inverse.
1177
+ * Transform a ground-plane point with a DOMMatrix, or reverse its screen projection.
1178
+ * Both branches divide by the homogeneous coordinate after applying the matrix.
1174
1179
  *
1175
1180
  * @param {L.Point|number[]} point Input pixel coordinate.
1176
- * @param {DOMMatrix|Object} matrix Forward or inverse transformation matrix.
1181
+ * @param {DOMMatrix|Object} matrix Forward CSS matrix or inverse homography with
1182
+ * row-major coefficients `a` through `i`.
1177
1183
  *
1178
1184
  * @returns {L.Point} Transformed pixel coordinate.
1179
1185
  *
@@ -1201,6 +1207,20 @@ var leafletRotate = (() => {
1201
1207
  return;
1202
1208
  }
1203
1209
  const pivot = this._getPixelCenter(), size = this.getSize(), perspective = Math.max(1, size.y * 1.5), position = this._rotatePane._leaflet_pos || new L.Point(0, 0), transform = `translate3d(${pivot.x}px,${pivot.y}px,0) perspective(${perspective}px) rotateX(${this.getPitch()}deg) rotateZ(${this.getBearing()}deg) translate3d(${position.x - pivot.x}px,${position.y - pivot.y}px,0)`;
1210
+ this._setPitchTransform(transform);
1211
+ },
1212
+ /**
1213
+ * Apply the CSS transform and cache matching forward/inverse point conversions.
1214
+ *
1215
+ * Ground points have z = 0, so the 4x4 CSS matrix reduces to the homography
1216
+ * [a b c; d e f; g h i]. Its inverse maps screen points back onto that plane;
1217
+ * inverting the full 4x4 matrix at screen z = 0 would not undo perspective.
1218
+ *
1219
+ * @param {string} transform CSS transform list, including a serialized DOMMatrix.
1220
+ *
1221
+ * @since leaflet-rotate (0.5.2)
1222
+ */
1223
+ _setPitchTransform(transform) {
1204
1224
  this._rotatePane.style.transformOrigin = "0 0";
1205
1225
  this._rotatePane.style.transform = transform;
1206
1226
  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);
@@ -1217,6 +1237,73 @@ var leafletRotate = (() => {
1217
1237
  };
1218
1238
  this._updatePitchSky();
1219
1239
  },
1240
+ /**
1241
+ * Capture a fixed projection baseline for a pitched drag or programmatic pan.
1242
+ *
1243
+ * `movestart` uses the viewport center; `dragstart` replaces it with the original
1244
+ * pointer position. Store both the screen anchor and its ground-plane coordinate
1245
+ * so subsequent frames can keep the grabbed geographic point under the pointer.
1246
+ * Pane positions are in screen pixels and the baseline matrices stay unchanged.
1247
+ *
1248
+ * @param {L.Event} e Leaflet `movestart` or `dragstart` event.
1249
+ *
1250
+ * @since leaflet-rotate (0.5.2)
1251
+ */
1252
+ _startPitchPan(e) {
1253
+ if (!this._pitch || !this._loaded) {
1254
+ return;
1255
+ }
1256
+ const draggable = this.dragging && this.dragging._draggable, anchor = e.type === "dragstart" && draggable ? (this.pointerEventToContainerPoint || this.mouseEventToContainerPoint).call(this, {
1257
+ clientX: draggable._startPoint.x,
1258
+ clientY: draggable._startPoint.y
1259
+ }) : this.getSize().divideBy(2), panePos = this._getMapPanePos().clone();
1260
+ this._pitchPan = {
1261
+ anchor,
1262
+ panePos,
1263
+ lastPanePos: panePos,
1264
+ matrix: this._pitchTransform,
1265
+ inverse: this._pitchInverseTransform,
1266
+ layerAnchor: this.containerPointToLayerPoint(anchor),
1267
+ zoom: this._zoom
1268
+ };
1269
+ },
1270
+ /**
1271
+ * Translate the ground plane without moving the camera's vanishing point.
1272
+ *
1273
+ * For pane displacement delta, invert the displaced anchor with the baseline
1274
+ * projection to find the required layer-space shift. Compose
1275
+ * T(-delta) * baseline * T(shift): the left translation cancels Leaflet's outer
1276
+ * pane movement, while the right translation moves geography before perspective.
1277
+ * Always derive this from the baseline, not the previous frame, to avoid drift.
1278
+ * Zoom frames are handled by Leaflet's zoom/origin updates instead.
1279
+ *
1280
+ * @since leaflet-rotate (0.5.2)
1281
+ */
1282
+ _updatePitchPan() {
1283
+ const pan = this._pitchPan;
1284
+ if (!this._pitch || !pan || this._animatingZoom || this._zoom !== pan.zoom) {
1285
+ return;
1286
+ }
1287
+ const panePos = this._getMapPanePos();
1288
+ if (panePos.equals(pan.lastPanePos)) {
1289
+ return;
1290
+ }
1291
+ const delta = panePos.subtract(pan.panePos), target = this._transformPitchPoint(pan.anchor.add(delta).subtract(pan.panePos), pan.inverse), shift = target.subtract(pan.layerAnchor);
1292
+ const matrix = new DOMMatrix().translate(-delta.x, -delta.y).multiply(pan.matrix).translate(shift.x, shift.y);
1293
+ this._setPitchTransform(matrix.toString());
1294
+ pan.lastPanePos = panePos.clone();
1295
+ delete this._lastCenter;
1296
+ },
1297
+ /**
1298
+ * Discard the baseline only at `moveend`, after any drag inertia has completed.
1299
+ * Keep the final transform and pixel origin: resetting the view here would
1300
+ * reproject layers abruptly and cause a visible jump on release.
1301
+ *
1302
+ * @since leaflet-rotate (0.5.2)
1303
+ */
1304
+ _endPitchPan() {
1305
+ delete this._pitchPan;
1306
+ },
1220
1307
  /**
1221
1308
  * Fill the area above the far plane (where no tiles are loaded) with a sky gradient.
1222
1309
  * Style it through the `.leaflet-pitch-sky` class.
@@ -1565,7 +1652,10 @@ var leafletRotate = (() => {
1565
1652
  // return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
1566
1653
  // },
1567
1654
  /**
1568
- * Return the pixel offset from the current center to the given geographic point.
1655
+ * Return the screen-space pan offset needed to center the given geographic point.
1656
+ * Under perspective, projecting a negated layer offset is not equivalent to
1657
+ * negating its screen projection. Reflect the target about the layer center
1658
+ * to match the opposite anchor displacement performed by Leaflet's `panBy`.
1569
1659
  *
1570
1660
  * @param {L.LatLng} latlng Geographic point.
1571
1661
  *
@@ -1577,6 +1667,10 @@ var leafletRotate = (() => {
1577
1667
  if (!this._rotate) {
1578
1668
  return mapProto._getCenterOffset.apply(this, arguments);
1579
1669
  }
1670
+ if (this._pitch) {
1671
+ const center = this.getSize().divideBy(2), layerCenter = this.containerPointToLayerPoint(center), opposite = layerCenter.multiplyBy(2).subtract(this.project(latlng).subtract(this._pixelOrigin));
1672
+ return center.subtract(this.layerPointToContainerPoint(opposite));
1673
+ }
1580
1674
  return this.latLngToContainerPoint(latlng)._subtract(this.getSize()._divideBy(2));
1581
1675
  },
1582
1676
  /**
@@ -1636,9 +1730,12 @@ var leafletRotate = (() => {
1636
1730
  return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
1637
1731
  },
1638
1732
  /**
1733
+ * Return the viewport center before the outer map-pane translation.
1734
+ * Used as the bearing/pitch pivot in map-pane coordinates, not layer coordinates.
1735
+ *
1639
1736
  * @since leaflet-rotate (v0.2.0)
1640
1737
  *
1641
- * @returns {L.Point} Map pivot point in pixel coordinates.
1738
+ * @returns {L.Point} Map pivot in map-pane pixels.
1642
1739
  */
1643
1740
  _getPixelCenter() {
1644
1741
  if (!this._rotate && mapProto._getPixelCenter) {
@@ -1647,6 +1744,9 @@ var leafletRotate = (() => {
1647
1744
  return this.getSize()._divideBy(2)._subtract(this._getMapPanePos());
1648
1745
  },
1649
1746
  /**
1747
+ * Convert the padded visible ground rectangle to layer bounds for vector renderers.
1748
+ * Clip its far edge before reversing perspective to avoid unbounded render areas.
1749
+ *
1650
1750
  * @see src\layer\vector\Renderer::_update()
1651
1751
  *
1652
1752
  * @param {number} padding Bounds padding as a fraction of map size.