Skip to content

Conversation

@HarelM
Copy link
Collaborator

@HarelM HarelM commented Dec 2, 2025

Launch Checklist

This changes the logic of tile refreshing for geojson
The idea behind this fix:

  1. When updating the data, we know exactly which geometries were changed, so we use this information to return the relevant bounds of these geometries
  2. It simplifies to only using bounds and not using ids of previous features
  • Confirm your changes do not include backports from Mapbox projects (unless with compliant license) - if you are not sure about this, please ask!
  • Briefly describe the changes in this PR.
  • Link to related issues.
  • Write tests for all new functionality.
  • Add an entry to CHANGELOG.md under the ## main section.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

@lucaswoj @wayofthefuture let me know what you think about the direction of this PR before I invest more time in fixing the tests and adding new tests.

@codecov
Copy link

codecov bot commented Dec 2, 2025

Codecov Report

❌ Patch coverage is 92.59259% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.41%. Comparing base (50d412d) to head (e430c63).

Files with missing lines Patch % Lines
src/source/geojson_source.ts 94.11% 1 Missing ⚠️
src/source/geojson_worker_source.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6800      +/-   ##
==========================================
- Coverage   92.42%   92.41%   -0.01%     
==========================================
  Files         288      288              
  Lines       23807    23803       -4     
  Branches     5056     5056              
==========================================
- Hits        22003    21998       -5     
- Misses       1804     1805       +1     

☔ 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.

@wayofthefuture
Copy link
Collaborator

I like the way this is headed.

However, let's say a feature was moved from 1 tile to another tile. How do you know to invalidate the previous tile without the feature id? The new bounds would not affect the previous tile.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

If a feature was moved from one tile to the other - I have the previous geometry just before I apply the update, and the new geometry.
So I "invalidate" tiles that have one of those geometries.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

I've gone and updated the relevant tests. I've removed some tests that are "duplicated" in the sense that they are being tested in lat lng bounds and there's no need to retest them in geojson source.
I've added tests that are using only public API to mimic how the class is behaving.
There's still a weird issue with the "covering world tile", which causes a test to fail.
I added a similar test to lnglatbounds tests, which also fails to highlight the issue in a simpler test.
@lucaswoj, do let me know what you think about this test and if there's something wrong about it.

@lucaswoj
Copy link
Contributor

lucaswoj commented Dec 2, 2025

Please hold off on merging these changes until I have time to review later today. I think this introduces some new bugs.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

Sure, take your time. I wouldn't merge this before you review it.

if (tile.state === 'unloaded') {
return false;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this codepath will cause performance regressions in some cases. Although having two codepaths is ugly, invalidating tiles by id is much faster and more accurate than doing so by bounds.

@lucaswoj
Copy link
Contributor

lucaswoj commented Dec 2, 2025

Thanks for diving into this code @HarelM! This does fix the bug but it may regress performance in some cases. I've proposed another fix at #6801, ready for your review, that is performance neutral.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

Can we measure how much performance degradation this is causing?
This code is a lot simpler to maintain and doesn't relay on feature index (including the timing related to it, which I find hard to make sure won't break in the future).

@lucaswoj
Copy link
Contributor

lucaswoj commented Dec 2, 2025

Can we measure how much performance degradation this is causing?

There's not a single number that captures the whole picture.

We can say that #6562 would no longer apply to sources loaded from a URL, so updating 1 point out of 100k points would become ~100% slower after this PR merges.

#6562 would also no longer apply in some cases involving more complex geometries, like an L-shaped line that crosses three out of 4 tiles in the viewport. We could create another benchmark that would become ~100% slower after this PR merges, for a non-URL-backed source.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

Fair enough, can you take a look at the lat lang bounds failing test? I'm not sure I understand why it's failing.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

It's worth mentioning that for tiles with a lot of features and small updates to the geojson, this route has better performance.
I'm not sure what I think about loading the geojson into memory when using url to improve performance, but that's also a possible route here.

@lucaswoj
Copy link
Contributor

lucaswoj commented Dec 2, 2025

Fair enough, can you take a look at the lat lang bounds failing test? I'm not sure I understand why it's failing.

Just created #6802 to investigate

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

@mwilsnd does the fix related to geojson undefined properties has to do with the feature index etc?
I'm wondering if the fix here is helps with your fix or if they are not related.

@mwilsnd
Copy link
Collaborator

mwilsnd commented Dec 2, 2025

Take a look: #6803 - this just changes how the layers are constructed in the feature index for geojson sources, I'm not sure if this is fully related.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 2, 2025

The features from the geojson are sent back for every tile, so it might be somewhat related, I'm not sure, I need to wrap my head around it...

@HarelM HarelM marked this pull request as ready for review December 3, 2025 07:34
* Refresh all tiles that PREVIOUSLY DID contain these feature ids.
*/
prevIds: Set<GeoJSON.Feature['id']>;
affectedBounds: LngLatBounds[];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since affectedBounds is the only property, would it be better to remove shouldReloadTileOptions and simply pass affectedBounds in the data event?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think keeping it this way allows adding properties in the future if needed, so I'm fine with keeping it that way.
I had the same thought myself, but decided to keep it as is.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if the change I made here makes sense now: 755ec5f

for (const feature of diff.add) {
const id = getFeatureId(feature, promoteId);
if (id != null) {
affectedGeometries.push(feature.geometry);
Copy link
Collaborator

@wayofthefuture wayofthefuture Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently updateable allows the addition of duplicate (replacing) feature ids, so you might want to consider checking/adding existing affected feature geometry here...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify a bit what you mean here?
I can move the push before the "if", if that's what you mean.

Copy link
Collaborator

@wayofthefuture wayofthefuture left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After giving this a look I can say that I definitely prefer this solution. Considering it's a geojson source, and geojson supports string ids, the fact that this also supports string ids just makes sense. It also saves us (and many) a breaking refactor to eliminate the warning. (correct me if I'm wrong).

Also, @HarelM's implementation is similar to my implementation used in the differential GeoJSON-VT PR that is pending. In this PR, updates are applied to the source and affected features are returned as well. Not sure if that would benefit in the future, but I like the idea of both sides using similar methods.

@HarelM
Copy link
Collaborator Author

HarelM commented Dec 4, 2025

Thanks for the review!
Note that I updated my initial message as feature updates were supported in the previous version, I just read the code wrong.

There are some performance changes (in some flows they are improved while in others they degrade).
I can't say which is better.
I can say that I think this approach is a bit less fragile as it doesn't relay on feature index.

I'll work on the review comments nevertheless.

@wayofthefuture
Copy link
Collaborator

#6809

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

geoJsonSource.updateData not working properly after updating to 5.13.0

5 participants