Skip to content

fix(globe): GeoJSON layers rendered twice near antimeridian#6970

Open
pabueco wants to merge 1 commit intomaplibre:mainfrom
pabueco:fix/globe-overdraw-antimeridian
Open

fix(globe): GeoJSON layers rendered twice near antimeridian#6970
pabueco wants to merge 1 commit intomaplibre:mainfrom
pabueco:fix/globe-overdraw-antimeridian

Conversation

@pabueco
Copy link
Copy Markdown

@pabueco pabueco commented Jan 15, 2026

This is an attempt to fix issue #6248

Note: While it seems to fix the issue for me I'm not sure how viable that approach/fix actually is.

The root cause for the bug outlined in the issue seems to be related to geojson-vt wrapping at the antimeridian and the fact that globe projection has no world copies. I first attempted to adjust the wrap function in geojson-vt to clip the features that cross the antimeridian, but that resulted in a overlap/line at the antimeridian, which I couldn't get rid of.

The approach that worked was leaving the geojson-vt wrapping as is and instead adjusting the stencil mode for tiles at/near the antimeridian to only draw on a pixel once per layer.

This removed the overdraw entirely (before and after):

image image

The code used in this example is a slightly adjusted codepen linked in the issue.

As you can see the last thing that remains are the vertical lines at the edge of the previous overdraw. I feel like this should be fixable in a similar way but using the same fix in draw_line.ts did not help.

However what did help was, changing the buffer value passed to geojson-vt to 256 (default is 128). Either in the workerOptions here or when adding the geojson source to the map:

map.addSource('rectangles', {
  'type': 'geojson',
  'data': geojson,
  'buffer': 256,
});

With this we get the exact result we're looking for:

image

Here is the benchmark result for LayerFill (I assumed this is the only relevant test case).

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.47%. Comparing base (482f8cb) to head (3f9f7a6).
⚠️ Report is 194 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6970   +/-   ##
=======================================
  Coverage   92.46%   92.47%           
=======================================
  Files         288      288           
  Lines       23856    23879   +23     
  Branches     5063     5073   +10     
=======================================
+ Hits        22058    22081   +23     
  Misses       1798     1798           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@HarelM
Copy link
Copy Markdown
Collaborator

HarelM commented Jan 19, 2026

We are working geojson-vt fixes right now, please let me know if there are things that might be better of moved there.
I saw the code related to wrapping, maybe it would make sense to add worldCopies boolean to geojson options to let it know that it doesn't need to replicate the features.
It will also make things a bit faster I believe.
CC: @wayofthefuture

@pabueco
Copy link
Copy Markdown
Author

pabueco commented Jan 19, 2026

@HarelM That's what I thought about as well at first, and it did generally work, but I couldn't get around the following (rendering) issue:

So in geojson-vt I modified the wrap function to split the features into a left and right part and used that when rendering a globe. In maplibre itself this approach would only require slight changes to switch the wrap mode when rendering the globe. I can open a PR for this approach as well, if you're interested. But anyway, this essentially was the change in geojson-vt:

export function wrapGlobe(features, options) {
    const result = [];
    
    const left = clip(features, 1, 0, 1, 0, -1, 2, options);
    if (left) result.push(...left);
    
    const right = clip(features, 1, 1, 2, 0, -1, 2, options);
    if (right) result.push(...shiftFeatureCoords(right, -1));
    
    return result;
}

This eliminates the double draw, but it introduces this line at the antimeridian for line layers. I guess this happens because we now have two features and they both receive their own outline.

image

But it's not just outlines, even if we just have a fill layer, we still get this very slight overlap at the antimeridian:

image

I could image this being solved when rendering, but I don't know how. Maybe you have an idea?

@HarelM
Copy link
Copy Markdown
Collaborator

HarelM commented Jan 19, 2026

Yeah, I see what you mean.
If the fix in geojson is simpler and "more correct" I think it would make sense to have that there, although I'm not sure how easy it would be to make sure it works when you switch between globe and mercator.
regarding the lines you said that in both solutions you see the overdraw, right?

CC: @mwilsnd, maybe he can take a look from a graphics perspective on this.

@pabueco
Copy link
Copy Markdown
Author

pabueco commented Jan 19, 2026

I guess the geojson-vt solution is simpler, because it doesn't involve stencils and it seems more correct, because it actually adjusts the underlying data instead of just eliminating the symptoms when rendering. I mainly went with the current fix, because it worked in maplibre directly and didn't involve another dependency.

Handling the switch between globe and mercator could be done by adding a onProjectionChange method to (GeoJSON)sources, which is then called by Style._setProjectionInternal and can be used to update the worker (options). I didn't test this thoroughly at the time, but it seemed to work.

Yes, with both solutions we get the vertical lines (either via overdraw of fill layers or the stroke on line layers). In the first stencil based solution they 'move' based on the set buffer size. In the second solution they are located exactly at the antimeridian, because we disabled the wrapping. This is similar to setting buffer: 0 with the first solution, but without the additional artefacts that disabling the buffer for the entire globe introduces.

@wayofthefuture
Copy link
Copy Markdown
Collaborator

@pabueco Not sure if you are aware but we onboarded geojson-vt to Maplibre and are actively developing on it. So we welcome any improvements!

@HarelM
Copy link
Copy Markdown
Collaborator

HarelM commented Mar 20, 2026

geojson-vt was on-borded and introduced into maplibre.
So fixing this issue there is possible if it's the right place.
@wayofthefuture mentioned that both geojson-vt and maplibre are wrapping features and we might want to remove the wrapping in the geojson-vt package and keep it only in maplibre.
I'm not sure I know what the right approach, but it seems that it might be a better solution, IDK.
@pabueco let me know if you would like to push this fix. If you need help setting anything up and testing please let me know.

@wayofthefuture
Copy link
Copy Markdown
Collaborator

With 5.21.0 I see no overlap with the reproduction above... not related but the globe fading is quite nice lol

@HarelM
Copy link
Copy Markdown
Collaborator

HarelM commented Mar 21, 2026

I took the codepen link from the linked issue and was still able to reproduce this with the latest version (5.21.0):

image

It requires some moving the globe around to make it visible, but not that hard to reproduce.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants