@@ -66,6 +66,8 @@ type Required<T> = ObjMap<T, <V>(v: V) => $NonMaybeType<V>>;
66
66
* This is useful for drawing attention to a location that is not in the screen center.
67
67
* `center` is ignored if `around` is included.
68
68
* @property {PaddingOptions } padding Dimensions in pixels applied on each side of the viewport for shifting the vanishing point.
69
+ * Note that when `padding` is used with `jumpTo`, `easeTo`, and `flyTo`, it also sets the global map padding as a side effect,
70
+ * affecting all subsequent camera movements until the padding is reset.
69
71
* @example
70
72
* // set the map's initial perspective with CameraOptions
71
73
* const map = new mapboxgl.Map({
@@ -152,8 +154,12 @@ export type ElevationBoxRaycast = {
152
154
const freeCameraNotSupportedWarning = 'map.setFreeCameraOptions(...) and map.getFreeCameraOptions() are not yet supported for non-mercator projections.' ;
153
155
154
156
/**
155
- * Options for setting padding on calls to methods such as {@link Map#fitBounds}, {@link Map#fitScreenCoordinates}, and {@link Map#setPadding}. Adjust these options to set the amount of padding in pixels added to the edges of the canvas. Set a uniform padding on all edges or individual values for each edge. All properties of this object must be
156
- * non-negative integers.
157
+ * Options for setting padding on calls to methods such as {@link Map#jumpTo}, {@link Map#easeTo}, {@link Map#flyTo},
158
+ * {@link Map#fitBounds}, {@link Map#fitScreenCoordinates}, and {@link Map#setPadding}. Adjust these options to set
159
+ * the amount of padding in pixels added to the edges of the canvas. Set a uniform padding on all edges or individual
160
+ * values for each edge. All properties of this object must be non-negative integers. Note that when `padding` is used with
161
+ * `fitBounds`, `flyTo`, or similar methods, it also sets the global map padding as a side effect, affecting all
162
+ * subsequent camera movements until the padding is reset.
157
163
*
158
164
* @typedef {Object } PaddingOptions
159
165
* @property {number } top Padding in pixels from the top of the map canvas.
@@ -182,6 +188,7 @@ class Camera extends Evented {
182
188
_zooming : boolean ;
183
189
_rotating : boolean ;
184
190
_pitching : boolean ;
191
+ _padding : boolean ;
185
192
186
193
_bearingSnap : number ;
187
194
_easeStart : number ;
@@ -605,29 +612,25 @@ class Camera extends Evented {
605
612
return this . _cameraForBounds ( this . transform , lnglat0 , lnglat1 , bearing , pitch , options ) ;
606
613
}
607
614
615
+ _extendPadding ( padding : ?PaddingOptions | ?number ) : Required < PaddingOptions > {
616
+ const defaultPadding = { top : 0 , right : 0 , bottom : 0 , left : 0 } ;
617
+ if ( padding == null ) return extend ( { } , defaultPadding , this . transform . padding ) ;
618
+
619
+ if ( typeof padding === 'number' ) {
620
+ return { top : padding , bottom : padding , right : padding , left : padding } ;
621
+ }
622
+
623
+ return extend ( { } , defaultPadding , padding ) ;
624
+ }
625
+
608
626
_extendCameraOptions ( options ? : CameraOptions ) : FullCameraOptions {
609
- const defaultPadding = {
610
- top : 0 ,
611
- bottom : 0 ,
612
- right : 0 ,
613
- left : 0
614
- } ;
615
627
options = extend ( {
616
- padding : defaultPadding ,
617
628
offset : [ 0 , 0 ] ,
618
629
maxZoom : this . transform . maxZoom
619
630
} , options ) ;
620
631
621
- if ( typeof options . padding === 'number' ) {
622
- const p = options . padding ;
623
- options . padding = {
624
- top : p ,
625
- bottom : p ,
626
- right : p ,
627
- left : p
628
- } ;
629
- }
630
- options . padding = extend ( defaultPadding , options . padding ) ;
632
+ options . padding = this . _extendPadding ( options . padding ) ;
633
+
631
634
return options ;
632
635
}
633
636
@@ -700,9 +703,13 @@ class Camera extends Evented {
700
703
const cameraToWorld = mat4 . invert ( new Float64Array ( 16 ) , worldToCamera ) ;
701
704
702
705
aabb = Aabb . applyTransform ( aabb , mat4 . multiply ( [ ] , worldToCamera , aabbOrientation ) ) ;
706
+ const extendedAabb = this . _extendAABB ( aabb , tr , eOptions , bearing ) ;
707
+ if ( ! extendedAabb ) {
708
+ warnOnce ( 'Map cannot fit within canvas with the given bounds, padding, and/or offset.' ) ;
709
+ return ;
710
+ }
703
711
704
- aabb = this . _extendAABBWithPaddings ( aabb , eOptions , tr , bearing ) ;
705
-
712
+ aabb = extendedAabb ;
706
713
vec3 . transformMat4 ( center , center , worldToCamera ) ;
707
714
708
715
const aabbHalfExtentZ = ( aabb . max [ 2 ] - aabb . min [ 2 ] ) * 0.5 ;
@@ -738,40 +745,62 @@ class Camera extends Evented {
738
745
return { center : tr . center , zoom, bearing, pitch} ;
739
746
}
740
747
741
- _extendAABBWithPaddings ( aabb : Aabb , eOptions : FullCameraOptions , tr : Transform , bearing : number ) : Aabb {
742
- const size = vec3 . sub ( [ ] , aabb . max , aabb . min ) ;
748
+ /**
749
+ * Extends the AABB with padding, offset, and bearing.
750
+ *
751
+ * @param {Aabb } aabb The AABB.
752
+ * @param {Transform } tr The transform.
753
+ * @param {FullCameraOptions } options Camera options.
754
+ * @param {number } bearing The bearing.
755
+ * @returns {Aabb | null } The extended AABB or null if couldn't scale.
756
+ * @private
757
+ */
758
+ _extendAABB ( aabb : Aabb , tr : Transform , options : FullCameraOptions , bearing : number ) : Aabb | null {
759
+ const padL = options . padding . left || 0 ;
760
+ const padR = options . padding . right || 0 ;
761
+ const padB = options . padding . bottom || 0 ;
762
+ const padT = options . padding . top || 0 ;
743
763
744
- const screenPadL = tr . padding . left || 0 ;
745
- const screenPadR = tr . padding . right || 0 ;
746
- const screenPadB = tr . padding . bottom || 0 ;
747
- const screenPadT = tr . padding . top || 0 ;
764
+ const halfScreenPadX = ( padL + padR ) * 0.5 ;
765
+ const halfScreenPadY = ( padT + padB ) * 0.5 ;
748
766
749
- const { left : padL , right : padR , top : padT , bottom : padB } = eOptions . padding ;
767
+ const top = halfScreenPadY ;
768
+ const left = halfScreenPadX ;
769
+ const right = halfScreenPadX ;
770
+ const bottom = halfScreenPadY ;
750
771
751
- const halfScreenPadX = ( screenPadL + screenPadR ) * 0.5 ;
752
- const halfScreenPadY = ( screenPadT + screenPadB ) * 0.5 ;
772
+ const width = tr . width - ( left + right ) ;
773
+ const height = tr . height - ( top + bottom ) ;
753
774
754
- const scaleX = ( tr . width - ( screenPadL + screenPadR + padL + padR ) ) / size [ 0 ] ;
755
- const scaleY = ( tr . height - ( screenPadB + screenPadT + padB + padT ) ) / size [ 1 ] ;
775
+ const aabbSize : [ number , number , number ] = vec3 . sub ( ( [ ] : any ) , aabb . max , aabb . min ) ;
756
776
757
- const zoomRef = Math . min ( tr . scaleZoom ( tr . scale * Math . min ( scaleX , scaleY ) ) , eOptions . maxZoom ) ;
777
+ const scaleX = width / aabbSize [ 0 ] ;
778
+ const scaleY = height / aabbSize [ 1 ] ;
779
+
780
+ const scale = Math . min ( scaleX , scaleY ) ;
781
+
782
+ const zoomRef = Math . min ( tr . scaleZoom ( tr . scale * scale ) , options . maxZoom ) ;
783
+ if ( isNaN ( zoomRef ) ) {
784
+ return null ;
785
+ }
758
786
759
787
const scaleRatio = tr . scale / tr . zoomScale ( zoomRef ) ;
760
788
761
- aabb = new Aabb (
762
- [ aabb . min [ 0 ] - ( padL + halfScreenPadX ) * scaleRatio , aabb . min [ 1 ] - ( padB + halfScreenPadY ) * scaleRatio , aabb . min [ 2 ] ] ,
763
- [ aabb . max [ 0 ] + ( padR + halfScreenPadX ) * scaleRatio , aabb . max [ 1 ] + ( padT + halfScreenPadY ) * scaleRatio , aabb . max [ 2 ] ] ) ;
789
+ const extendedAABB = new Aabb (
790
+ [ aabb . min [ 0 ] - left * scaleRatio , aabb . min [ 1 ] - bottom * scaleRatio , aabb . min [ 2 ] ] ,
791
+ [ aabb . max [ 0 ] + right * scaleRatio , aabb . max [ 1 ] + top * scaleRatio , aabb . max [ 2 ] ]
792
+ ) ;
764
793
765
- const centerOffset = ( typeof eOptions . offset . x === 'number' && typeof eOptions . offset . y === 'number' ) ?
766
- new Point ( eOptions . offset . x , eOptions . offset . y ) :
767
- Point . convert ( eOptions . offset ) ;
794
+ const centerOffset = ( typeof options . offset . x === 'number' && typeof options . offset . y === 'number' ) ?
795
+ new Point ( options . offset . x , options . offset . y ) :
796
+ Point . convert ( options . offset ) ;
768
797
769
798
const rotatedOffset = centerOffset . rotate ( - degToRad ( bearing ) ) ;
770
799
771
- aabb . center [ 0 ] -= rotatedOffset . x * scaleRatio ;
772
- aabb . center [ 1 ] += rotatedOffset . y * scaleRatio ;
800
+ extendedAABB . center [ 0 ] -= rotatedOffset . x * scaleRatio ;
801
+ extendedAABB . center [ 1 ] += rotatedOffset . y * scaleRatio ;
773
802
774
- return aabb ;
803
+ return extendedAABB ;
775
804
}
776
805
777
806
/** @section {Querying features} */
@@ -862,9 +891,13 @@ class Camera extends Evented {
862
891
const cameraToWorld = mat4 . invert ( new Float64Array ( 16 ) , worldToCamera ) ;
863
892
864
893
aabb = Aabb . applyTransform ( aabb , worldToCamera ) ;
894
+ const extendedAabb = this . _extendAABB ( aabb , tr , eOptions , bearing ) ;
895
+ if ( ! extendedAabb ) {
896
+ warnOnce ( 'Map cannot fit within canvas with the given bounds, padding, and/or offset.' ) ;
897
+ return ;
898
+ }
865
899
866
- aabb = this . _extendAABBWithPaddings ( aabb , eOptions , tr , bearing ) ;
867
-
900
+ aabb = extendedAabb ;
868
901
const size = vec3 . sub ( [ ] , aabb . max , aabb . min ) ;
869
902
const aabbHalfExtentZ = size [ 2 ] * 0.5 ;
870
903
const frustumDistance = this . _minimumAABBFrustumDistance ( tr , aabb ) ;
@@ -1000,8 +1033,6 @@ class Camera extends Evented {
1000
1033
if ( ! calculatedOptions ) return this ;
1001
1034
1002
1035
options = extend ( calculatedOptions , options ) ;
1003
- // Explicitly remove the padding field because, calculatedOptions already accounts for padding by setting zoom and center accordingly.
1004
- delete options . padding ;
1005
1036
1006
1037
return options . linear ?
1007
1038
this . easeTo ( options , eventData ) :
@@ -1267,7 +1298,7 @@ class Camera extends Evented {
1267
1298
zoom = 'zoom' in options ? + options . zoom : startZoom ,
1268
1299
bearing = 'bearing' in options ? this . _normalizeBearing ( options . bearing , startBearing ) : startBearing ,
1269
1300
pitch = 'pitch' in options ? + options . pitch : startPitch ,
1270
- padding = 'padding' in options ? options . padding : tr . padding ;
1301
+ padding = this . _extendPadding ( options . padding ) ;
1271
1302
1272
1303
const offsetAsPoint = Point . convert ( options . offset ) ;
1273
1304
@@ -1365,6 +1396,7 @@ class Camera extends Evented {
1365
1396
this . _zooming = zoomChanged ;
1366
1397
this . _rotating = bearingChanged ;
1367
1398
this . _pitching = pitchChanged ;
1399
+ this . _padding = paddingChanged ;
1368
1400
1369
1401
this . _easeId = options . easeId ;
1370
1402
this . _prepareEase ( eventData , options . noMoveStart , currently ) ;
@@ -1429,6 +1461,7 @@ class Camera extends Evented {
1429
1461
this . _zooming = false ;
1430
1462
this . _rotating = false ;
1431
1463
this . _pitching = false ;
1464
+ this . _padding = false ;
1432
1465
1433
1466
if ( wasZooming ) {
1434
1467
this . fire ( new Event ( 'zoomend' , eventData ) ) ;
@@ -1528,25 +1561,19 @@ class Camera extends Evented {
1528
1561
const tr = this . transform ,
1529
1562
startZoom = this . getZoom ( ) ,
1530
1563
startBearing = this . getBearing ( ) ,
1531
- startPitch = this . getPitch ( ) ;
1564
+ startPitch = this . getPitch ( ) ,
1565
+ startPadding = this . getPadding ( ) ;
1532
1566
1533
1567
const zoom = 'zoom' in options ? clamp ( + options . zoom , tr . minZoom , tr . maxZoom ) : startZoom ;
1534
1568
const bearing = 'bearing' in options ? this . _normalizeBearing ( options . bearing , startBearing ) : startBearing ;
1535
1569
const pitch = 'pitch' in options ? + options . pitch : startPitch ;
1570
+ const padding = this . _extendPadding ( options . padding ) ;
1536
1571
1537
1572
const scale = tr . zoomScale ( zoom - startZoom ) ;
1538
1573
const offsetAsPoint = Point . convert ( options . offset ) ;
1539
- const pointAtOffset = tr . centerPoint . add ( offsetAsPoint ) ;
1574
+ let pointAtOffset = tr . centerPoint . add ( offsetAsPoint ) ;
1540
1575
const locationAtOffset = tr . pointLocation ( pointAtOffset ) ;
1541
-
1542
- let center = options . center ;
1543
- // Calculate center with respect to padding
1544
- if ( center && options . padding ) {
1545
- const easingOptions = this . _cameraForBounds ( this . transform , center , center , bearing , pitch , options ) ;
1546
- if ( easingOptions ) center = easingOptions . center ;
1547
- }
1548
-
1549
- center = LngLat . convert ( center || locationAtOffset ) ;
1576
+ const center = LngLat . convert ( options . center || locationAtOffset ) ;
1550
1577
this . _normalizeCenter ( center ) ;
1551
1578
1552
1579
const from = tr . project ( locationAtOffset ) ;
@@ -1632,6 +1659,7 @@ class Camera extends Evented {
1632
1659
const zoomChanged = true ;
1633
1660
const bearingChanged = ( startBearing !== bearing ) ;
1634
1661
const pitchChanged = ( pitch !== startPitch ) ;
1662
+ const paddingChanged = ! tr . isPaddingEqual ( padding ) ;
1635
1663
1636
1664
const frame = ( tr : Transform ) => ( k : number ) => {
1637
1665
// s: The distance traveled along the flight path, measured in ρ-screenfuls.
@@ -1645,6 +1673,12 @@ class Camera extends Evented {
1645
1673
if ( pitchChanged ) {
1646
1674
tr . pitch = interpolate ( startPitch , pitch , k ) ;
1647
1675
}
1676
+ if ( paddingChanged ) {
1677
+ tr . interpolatePadding ( startPadding , padding , k ) ;
1678
+ // When padding is being applied, Transform#centerPoint is changing continuously,
1679
+ // thus we need to recalculate offsetPoint every frame
1680
+ pointAtOffset = tr . centerPoint . add ( offsetAsPoint ) ;
1681
+ }
1648
1682
1649
1683
const newCenter = k === 1 ? center : tr . unproject ( from . add ( delta . mult ( u ( s ) ) ) . mult ( scale ) ) ;
1650
1684
tr . setLocationAtPoint ( tr . renderWorldCopies ? newCenter . wrap ( ) : newCenter , pointAtOffset ) ;
@@ -1666,6 +1700,7 @@ class Camera extends Evented {
1666
1700
this . _zooming = zoomChanged ;
1667
1701
this . _rotating = bearingChanged ;
1668
1702
this . _pitching = pitchChanged ;
1703
+ this . _padding = paddingChanged ;
1669
1704
1670
1705
this . _prepareEase ( eventData , false ) ;
1671
1706
this . _ease ( frame ( tr ) , ( ) => this . _afterEase ( eventData ) , options ) ;
0 commit comments