-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Match GlobeView projection parameters with Maplibre v5 #9201
base: x/globe-test-app
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,9 @@ import {MapState, MapStateProps} from './map-controller'; | |
import {mod} from '../utils/math-utils'; | ||
import LinearInterpolator from '../transitions/linear-interpolator'; | ||
|
||
// matches Web Mercator projection limit | ||
const MAX_VALID_LATITUDE = 85.051129; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm seeing strange things when moving near the poles Screen.Recording.2024-10-04.at.10.40.03.mov |
||
class GlobeState extends MapState { | ||
// Apply any constraints (mathematical or defined by _viewportProps) to map state | ||
applyConstraints(props: Required<MapStateProps>): Required<MapStateProps> { | ||
|
@@ -20,7 +23,7 @@ class GlobeState extends MapState { | |
if (longitude < -180 || longitude > 180) { | ||
props.longitude = mod(longitude + 180, 360) - 180; | ||
} | ||
props.latitude = clamp(latitude, -89, 89); | ||
props.latitude = clamp(latitude, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we go all the way? or do we need some offset to avoid issues? |
||
|
||
return props; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
import {Matrix4} from '@math.gl/core'; | ||
import Viewport from './viewport'; | ||
import {PROJECTION_MODE} from '../lib/constants'; | ||
import {altitudeToFovy, fovyToAltitude} from '@math.gl/web-mercator'; | ||
|
||
import {vec3, vec4} from '@math.gl/core'; | ||
|
||
|
@@ -50,6 +51,8 @@ export type GlobeViewportOptions = { | |
zoom?: number; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth adding a comment about zoom's range in a globe view? |
||
/** Use orthographic projection */ | ||
orthographic?: boolean; | ||
/** Camera fovy in degrees. If provided, overrides `altitude` */ | ||
fovy?: number; | ||
/** Scaler for the near plane, 1 unit equals to the height of the viewport. Default `0.1` */ | ||
nearZMultiplier?: number; | ||
/** Scaler for the far plane, 1 unit equals to the distance from the camera to the edge of the screen. Default `2` */ | ||
|
@@ -59,37 +62,39 @@ export type GlobeViewportOptions = { | |
}; | ||
|
||
export default class GlobeViewport extends Viewport { | ||
// @ts-ignore | ||
longitude: number; | ||
// @ts-ignore | ||
latitude: number; | ||
resolution: number; | ||
longitude!: number; | ||
latitude!: number; | ||
resolution!: number; | ||
|
||
constructor(opts: GlobeViewportOptions = {}) { | ||
const { | ||
latitude = 0, | ||
longitude = 0, | ||
zoom = 0, | ||
nearZMultiplier = 0.1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you document somewhere how you updated these defaults? Is there some magic formula? |
||
farZMultiplier = 2, | ||
nearZMultiplier = 0.5, | ||
farZMultiplier = 1, | ||
resolution = 10 | ||
} = opts; | ||
|
||
let {height, altitude = 1.5} = opts; | ||
let {height, altitude = 1.5, fovy} = opts; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: FWIW, I have consistently been adding a defaultOptions object to classes in luma.gl so that I can document all default options in one place rather than spreading out the default values in a bunch of descructuring calls like this. static defaultOptions: Required<GlobeViewportOptions> = {
...
};
this.options ={...GlobeViewport.defaultOptions, ...options}; |
||
|
||
height = height || 1; | ||
altitude = Math.max(0.75, altitude); | ||
if (fovy) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This aligns us more with other views, right? |
||
altitude = fovyToAltitude(fovy); | ||
} else { | ||
fovy = altitudeToFovy(altitude); | ||
} | ||
// Used to match globe and web mercator projection at high zoom | ||
const scaleAdjust = 1 / Math.PI / Math.cos((latitude * Math.PI) / 180); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth guarding against division by 0? Despite this, the viewport could still be manually constructed with the pole value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have had a LOT of problems over the years in deck and kepler with mercator viewports throwing due to bad inputs somehow being generated - every time it arises, it confuses a couple of new engineers and PMs, tickets get created, discussions in sprint reviews etc - ultimately hours of time are lost and little is done. So I agree that avoiding the issue would be great. The question is what to do instead. Force the value to the closest value that will work? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the new math logic? Almost missed it. Not sure we can put this in a separate method or something to make it stand out mode? |
||
const scale = Math.pow(2, zoom) * scaleAdjust; | ||
const farZ = altitude + (GLOBE_RADIUS * 2 * scale) / height; | ||
|
||
// Calculate view matrix | ||
const viewMatrix = new Matrix4().lookAt({eye: [0, -altitude, 0], up: [0, 0, 1]}); | ||
const scale = Math.pow(2, zoom); | ||
viewMatrix.rotateX(latitude * DEGREES_TO_RADIANS); | ||
viewMatrix.rotateZ(-longitude * DEGREES_TO_RADIANS); | ||
viewMatrix.scale(scale / height); | ||
|
||
const halfFov = Math.atan(0.5 / altitude); | ||
const relativeScale = (GLOBE_RADIUS * 2 * scale) / height; | ||
|
||
super({ | ||
...opts, | ||
// x, y, width, | ||
|
@@ -103,12 +108,13 @@ export default class GlobeViewport extends Viewport { | |
|
||
// projection matrix parameters | ||
distanceScales: getDistanceScales(), | ||
fovyRadians: halfFov * 2, | ||
fovy, | ||
focalDistance: altitude, | ||
near: nearZMultiplier, | ||
far: Math.min(2, 1 / relativeScale + 1) * altitude * farZMultiplier | ||
far: farZ * farZMultiplier | ||
}); | ||
|
||
this.scale = scale; | ||
this.latitude = latitude; | ||
this.longitude = longitude; | ||
this.resolution = resolution; | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -4,6 +4,7 @@ | |||||
|
||||||
import View, {CommonViewState, CommonViewProps} from './view'; | ||||||
import GlobeViewport from '../viewports/globe-viewport'; | ||||||
import WebMercatorViewport from '../viewports/web-mercator-viewport'; | ||||||
import GlobeController from '../controllers/globe-controller'; | ||||||
|
||||||
export type GlobeViewState = { | ||||||
|
@@ -37,8 +38,8 @@ export default class GlobeView extends View<GlobeViewState, GlobeViewProps> { | |||||
super(props); | ||||||
} | ||||||
|
||||||
get ViewportType() { | ||||||
return GlobeViewport; | ||||||
getViewportType(viewState: GlobeViewState) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
return viewState.zoom > 12 ? WebMercatorViewport : GlobeViewport; | ||||||
} | ||||||
|
||||||
get ControllerType() { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -275,7 +275,7 @@ const TEST_CASES = [ | |
viewState: { | ||
longitude: -6, | ||
latitude: 58, | ||
zoom: 1.5 | ||
zoom: 1.5 + 0.7353406094252244 // Math.log2(1 / Math.PI / Math.cos(58 / 180 * Math.PI)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel it is better to update the image than having some weird constant here. There is a significant change in output anyway There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this constant help see us abnormalities in the images, or have some other benefit for testing? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice if comment explained what the formula is doing in a few words. |
||
} | ||
}), | ||
tileSize: 512, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,9 +101,7 @@ export default [ | |
viewState: { | ||
longitude: -100, | ||
latitude: 80, | ||
zoom: 0, | ||
pitch: 0, | ||
bearing: 0 | ||
zoom: -1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's a negative zoom mean for globe? |
||
}, | ||
layers: [ | ||
new SimpleMeshLayer({ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if math.gl shouldn't export this - it is used in a number of places
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 - we have a web-mercator module after all, and this is one of the primary web-mercator constants.