@@ -17,6 +17,7 @@ import type {IReadonlyTransform, ITransform} from '../transform_interface';
17
17
import type { PaddingOptions } from '../edge_insets' ;
18
18
import type { ProjectionData , ProjectionDataParams } from './projection_data' ;
19
19
import type { CoveringTilesDetailsProvider } from './covering_tiles_details_provider' ;
20
+ import versor from 'versor' ;
20
21
21
22
/**
22
23
* Describes the intersection of ray and sphere.
@@ -218,14 +219,14 @@ export class VerticalPerspectiveTransform implements ITransform {
218
219
get renderWorldCopies ( ) : boolean {
219
220
return this . _helper . renderWorldCopies ;
220
221
}
221
- public get nearZ ( ) : number {
222
- return this . _helper . nearZ ;
222
+ public get nearZ ( ) : number {
223
+ return this . _helper . nearZ ;
223
224
}
224
- public get farZ ( ) : number {
225
- return this . _helper . farZ ;
225
+ public get farZ ( ) : number {
226
+ return this . _helper . farZ ;
226
227
}
227
- public get autoCalculateNearFarZ ( ) : boolean {
228
- return this . _helper . autoCalculateNearFarZ ;
228
+ public get autoCalculateNearFarZ ( ) : boolean {
229
+ return this . _helper . autoCalculateNearFarZ ;
229
230
}
230
231
setTransitionState ( _value : number ) : void {
231
232
// Do nothing
@@ -659,100 +660,25 @@ export class VerticalPerspectiveTransform implements ITransform {
659
660
* Note: automatically adjusts zoom to keep planet size consistent
660
661
* (same size before and after a {@link setLocationAtPoint} call).
661
662
*/
662
- setLocationAtPoint ( lnglat : LngLat , point : Point ) : void {
663
- // This returns some fake coordinates for pixels that do not lie on the planet.
664
- // Whatever uses this `setLocationAtPoint` function will need to account for that.
665
- const pointLngLat = this . unprojectScreenPoint ( point ) ;
666
- const vecToPixelCurrent = angularCoordinatesToSurfaceVector ( pointLngLat ) ;
667
- const vecToTarget = angularCoordinatesToSurfaceVector ( lnglat ) ;
668
-
669
- const zero = createVec3f64 ( ) ;
670
- vec3 . zero ( zero ) ;
671
-
672
- const rotatedPixelVector = createVec3f64 ( ) ;
673
- vec3 . rotateY ( rotatedPixelVector , vecToPixelCurrent , zero , - this . center . lng * Math . PI / 180.0 ) ;
674
- vec3 . rotateX ( rotatedPixelVector , rotatedPixelVector , zero , this . center . lat * Math . PI / 180.0 ) ;
675
-
676
- // We are looking for the lng,lat that will rotate `vecToTarget`
677
- // so that it is equal to `rotatedPixelVector`.
678
-
679
- // The second rotation around X axis cannot change the X component,
680
- // so we first must find the longitude such that rotating `vecToTarget` with it
681
- // will place it so its X component is equal to X component of `rotatedPixelVector`.
682
- // There will exist zero, one or two longitudes that satisfy this.
683
-
684
- // x |
685
- // / |
686
- // / | the line is the target X - rotatedPixelVector.x
687
- // / | the x is vecToTarget projected to x,z plane
688
- // . | the dot is origin
689
- //
690
- // We need to rotate vecToTarget so that it intersects the line.
691
- // If vecToTarget is shorter than the distance to the line from origin, it is impossible.
692
-
693
- // Otherwise, we compute the intersection of the line with a ring with radius equal to
694
- // length of vecToTarget projected to XZ plane.
695
-
696
- const vecToTargetXZLengthSquared = vecToTarget [ 0 ] * vecToTarget [ 0 ] + vecToTarget [ 2 ] * vecToTarget [ 2 ] ;
697
- const targetXSquared = rotatedPixelVector [ 0 ] * rotatedPixelVector [ 0 ] ;
698
- if ( vecToTargetXZLengthSquared < targetXSquared ) {
699
- // Zero solutions - setLocationAtPoint is impossible.
700
- return ;
701
- }
702
-
703
- // The intersection's Z coordinates
704
- const intersectionA = Math . sqrt ( vecToTargetXZLengthSquared - targetXSquared ) ;
705
- const intersectionB = - intersectionA ; // the second solution
706
-
707
- const lngA = angleToRotateBetweenVectors2D ( vecToTarget [ 0 ] , vecToTarget [ 2 ] , rotatedPixelVector [ 0 ] , intersectionA ) ;
708
- const lngB = angleToRotateBetweenVectors2D ( vecToTarget [ 0 ] , vecToTarget [ 2 ] , rotatedPixelVector [ 0 ] , intersectionB ) ;
709
-
710
- const vecToTargetLngA = createVec3f64 ( ) ;
711
- vec3 . rotateY ( vecToTargetLngA , vecToTarget , zero , - lngA ) ;
712
- const latA = angleToRotateBetweenVectors2D ( vecToTargetLngA [ 1 ] , vecToTargetLngA [ 2 ] , rotatedPixelVector [ 1 ] , rotatedPixelVector [ 2 ] ) ;
713
- const vecToTargetLngB = createVec3f64 ( ) ;
714
- vec3 . rotateY ( vecToTargetLngB , vecToTarget , zero , - lngB ) ;
715
- const latB = angleToRotateBetweenVectors2D ( vecToTargetLngB [ 1 ] , vecToTargetLngB [ 2 ] , rotatedPixelVector [ 1 ] , rotatedPixelVector [ 2 ] ) ;
716
- // Is at least one of the needed latitudes valid?
717
-
718
- const limit = Math . PI * 0.5 ;
719
-
720
- const isValidA = latA >= - limit && latA <= limit ;
721
- const isValidB = latB >= - limit && latB <= limit ;
722
-
723
- let validLng : number ;
724
- let validLat : number ;
725
- if ( isValidA && isValidB ) {
726
- // Pick the solution that is closer to current map center.
727
- const centerLngRadians = this . center . lng * Math . PI / 180.0 ;
728
- const centerLatRadians = this . center . lat * Math . PI / 180.0 ;
729
- const lngDistA = distanceOfAnglesRadians ( lngA , centerLngRadians ) ;
730
- const latDistA = distanceOfAnglesRadians ( latA , centerLatRadians ) ;
731
- const lngDistB = distanceOfAnglesRadians ( lngB , centerLngRadians ) ;
732
- const latDistB = distanceOfAnglesRadians ( latB , centerLatRadians ) ;
733
-
734
- if ( ( lngDistA + latDistA ) < ( lngDistB + latDistB ) ) {
735
- validLng = lngA ;
736
- validLat = latA ;
737
- } else {
738
- validLng = lngB ;
739
- validLat = latB ;
740
- }
741
- } else if ( isValidA ) {
742
- validLng = lngA ;
743
- validLat = latA ;
744
- } else if ( isValidB ) {
745
- validLng = lngB ;
746
- validLat = latB ;
747
- } else {
748
- // No solution.
749
- return ;
663
+ setLocationAtPoint ( sourceLnglat : LngLat , targetPoint : Point ) : void {
664
+ const targetLngLat = this . unprojectScreenPoint ( targetPoint ) ;
665
+ const reprojTargetPoint = this . locationToScreenPoint ( targetLngLat ) ;
666
+ if ( Math . abs ( targetPoint . x - reprojTargetPoint . x ) > 0.001 || Math . abs ( targetPoint . y - reprojTargetPoint . y ) > 0.001 ) {
667
+ return ; // The point is not on the planet
750
668
}
751
669
752
- const newLng = validLng / Math . PI * 180 ;
753
- const newLat = validLat / Math . PI * 180 ;
670
+ const sourceCartesian = versor . cartesian ( [ sourceLnglat . lng , sourceLnglat . lat ] ) ;
671
+ const targetCartesian = versor . cartesian ( [ targetLngLat . lng , targetLngLat . lat ] ) ;
672
+ const delta = versor . delta ( sourceCartesian , targetCartesian ) ;
673
+ const centerQuaternion = versor ( [ - this . center . lng , - this . center . lat , this . bearing ] ) ;
674
+ const newCenterQuaternion = versor . multiply ( centerQuaternion , delta ) ;
675
+ const [ lambda , phi , gamma ] = versor . rotation ( newCenterQuaternion ) ;
676
+ const newLng = - lambda ;
677
+ const newLat = - phi ;
678
+ const newBearing = gamma ;
754
679
const oldLat = this . center . lat ;
755
- this . setCenter ( new LngLat ( newLng , clamp ( newLat , - 90 , 90 ) ) ) ;
680
+ this . setCenter ( new LngLat ( newLng , newLat ) ) ;
681
+ this . setBearing ( newBearing ) ;
756
682
this . setZoom ( this . zoom + getZoomAdjustment ( oldLat , this . center . lat ) ) ;
757
683
}
758
684
0 commit comments