You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am not able to properly visualize IconLayers on my Globe-projected DeckGL instance.
In my React-Maplibre-MapboxOverlay implementation mercator projection shows up great, but globe projection glitches out. (tested both interleaved/overlaid)
Screen.Recording.2025-03-29.at.4.14.08.PM.mov
In my (codepen using script tag) reproduction, I am actually able to see the point when the globe is oriented such that the Icon extrudes in the right direction, however, in my implementation, the point disappears after any user interaction.
Additionally, in the reproduction, the IconLayer instances do not show up in the right location. Notice how the coordinates of the sample points are all within Florida, however, the IconLayer shows up at the origin
I managed to work around this by turning off depthTest and filter my dataset with this method based on the camera position:
// Helper function to determine if a feature is visible from a camera's pov on globe projectionfunctionisFeatureVisibleOnGlobe(cameraLat: number,cameraLon: number,featureLat: number,featureLon: number,zoom: number,): boolean{consttoRad=(deg: number)=>(deg*Math.PI)/180;// Convert lat/lon to radiansconstcamLatRad=toRad(cameraLat);constcamLonRad=toRad(cameraLon);constfeatLatRad=toRad(featureLat);constfeatLonRad=toRad(featureLon);// Convert to unit vectorsconsttoVec3=(lat: number,lon: number): [number,number,number]=>{return[Math.cos(lat)*Math.cos(lon),Math.sin(lat),Math.cos(lat)*Math.sin(lon),];};constcamVec=toVec3(camLatRad,camLonRad);constfeatVec=toVec3(featLatRad,featLonRad);// Compute dot productconstdot=camVec[0]*featVec[0]+camVec[1]*featVec[1]+camVec[2]*featVec[2];// Convert zoom level to a tighter FOV thresholdconstfovDegrees=zoomToFOV(zoom);// field of view in degreesconsthalfFovCos=Math.cos(toRad(fovDegrees/2));// If the angle between the vectors is within the cone, dot ≥ cos(halfFOV)returndot>=halfFovCos;}// Tune this mapping to match your renderer behaviorfunctionzoomToFOV(zoom: number): number{constclamped=Math.max(Math.min(zoom,20),1);// At zoom 1 → full hemisphere (≈130° FOV), at zoom 20 → tight 0° FOVreturn130*(1-(clamped-1)/19);// Range: 130 → 0 degrees}
//assumes data is a geojson feature collection with Point geometriesnewIconClusterLayer<GeoJSON.Feature<GeoJSON.MultiPoint,GeoJSON.GeoJsonProperties>>({data: data.features.flatMap((feature)=>feature.geometry.coordinates.map((coordinate)=>({type: "Feature",geometry: {type: "Point",coordinates: coordinate,},properties: feature.properties,})),).filter((feature)=>{const[lng,lat]=feature.geometry.coordinates;returnisFeatureVisibleOnGlobe(vs.longitude,vs.latitude,lng,lat,vs.zoom,);}),
...
parameters: {depthTest: false,},}),
Additionally, in the reproduction, the IconLayer instances do not show up in the right location. Notice how the coordinates of the sample points are all within Florida, however, the IconLayer shows up at the origin
This sub-issue is fixed with this accessor: getPosition: d => d.geometry.coordinates,
The main issue seems to be that the layers are projected onto a 3D sphere, and the icon's z is anchored on that sphere. So, as a consequence, when the globe is tilted/pulled towards the camera, the icon is blocked by the tiles that are "in front" of the anchor.
I spent some time today trying a variety of parameters to solve this without filtering based on camera position.. I wasn't successful. I think the problem is a general issue with billboard-style rendering combined with how the anchor point on the globe contributes to the depth buffer. If the icon were either wrapped around the sphere like the tile meshes are, then maybe this wouldn't be an issue.
For illustration purposes, setting a ridiculous z (`getPosition: d => [0,0, 10000000],) will prevent occlusion.
@charlieforward9 the video and workaround you shared looks great and even fades the icon's opacity as it approaches the edge (is that code not included in your snippet?). I'm not sure what the generic solution for this is, but doing a camera-space test like you're doing does seem like a reliable method.
@charlieforward9 Do you know, if are there any updates regarding globe projection support for the IconLayer in deck.gl? If not, can you recomment any alternative layer I could use without the need for the camera-space test?
I've removed this from the 9.3 roadmap, and think it's worth revisiting. I've linked this issue with the other two related issues. I'm not convinced the fix proposed in #9975 looks better than the current workaround. The important requirements include:
In interleaved and non-interleaved mode:
the globe shouldn't clip foreground billboard layers
the globe should trigger automatic occlusion on billboard layers when they're beyond the horizon
@chrisgervang i appreciate you looking into this in more detail and stepping back to try different approaches.
video and workaround you shared looks great and even fades the icon's opacity as it approaches the edge (is that code not included in your snippet?). I'm not sure what the generic solution for this is, but doing a camera-space test like you're doing does seem like a reliable method.
The original workaround I built was to do layer-specific filtering based on the dot product of the data coordinate and viewstate with an additional filter on the opacity field to "transition fade".
This is a GlobeView specific workaround. I wonder if we could
try to land something like this as an extension or a GlobeView patch with a combination of our approaches?
Just trying to keep this conversation moving forward
Activity
charlieforward9 commentedon Apr 8, 2025
I managed to work around this by turning off
depthTestand filter my dataset with this method based on the camera position:Globe.Spin.mp4
chrisgervang commentedon Jun 3, 2025
This sub-issue is fixed with this accessor:
getPosition: d => d.geometry.coordinates,The main issue seems to be that the layers are projected onto a 3D sphere, and the icon's z is anchored on that sphere. So, as a consequence, when the globe is tilted/pulled towards the camera, the icon is blocked by the tiles that are "in front" of the anchor.
I spent some time today trying a variety of parameters to solve this without filtering based on camera position.. I wasn't successful. I think the problem is a general issue with billboard-style rendering combined with how the anchor point on the globe contributes to the depth buffer. If the icon were either wrapped around the sphere like the tile meshes are, then maybe this wouldn't be an issue.
For illustration purposes, setting a ridiculous
z(`getPosition: d => [0,0, 10000000],) will prevent occlusion.@charlieforward9 the video and workaround you shared looks great and even fades the icon's opacity as it approaches the edge (is that code not included in your snippet?). I'm not sure what the generic solution for this is, but doing a camera-space test like you're doing does seem like a reliable method.
tmrdlt commentedon Feb 3, 2026
@charlieforward9 Do you know, if are there any updates regarding globe projection support for the
IconLayerin deck.gl? If not, can you recomment any alternative layer I could use without the need for the camera-space test?chrisgervang commentedon Feb 3, 2026
Hey @tmrdlt, thanks for resurfacing this issue. I've drafted a fix in #9975. I'll test out the gist shared here
chrisgervang commentedon Apr 1, 2026
I've removed this from the 9.3 roadmap, and think it's worth revisiting. I've linked this issue with the other two related issues. I'm not convinced the fix proposed in #9975 looks better than the current workaround. The important requirements include:
In interleaved and non-interleaved mode:
The PR satisfied 2, but not 1.
charlieforward9 commentedon Apr 1, 2026
@chrisgervang i appreciate you looking into this in more detail and stepping back to try different approaches.
The original workaround I built was to do layer-specific filtering based on the dot product of the data coordinate and viewstate with an additional filter on the opacity field to "transition fade".
This is a GlobeView specific workaround. I wonder if we could
try to land something like this as an extension or a GlobeView patch with a combination of our approaches?
Just trying to keep this conversation moving forward