From dac23b1e010e3f2a433b07e9d223efaa1baff302 Mon Sep 17 00:00:00 2001 From: Jacob Jeppesen Date: Tue, 24 Jun 2025 09:41:26 +0200 Subject: [PATCH] Added first version of raster-fade-duration bug fix --- src/source/source_cache.test.ts | 47 ++++++++++++++++++++++++++++----- src/source/source_cache.ts | 23 +++++++++++++--- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/source/source_cache.test.ts b/src/source/source_cache.test.ts index 6cc88074d36..8c5d5fe68d2 100644 --- a/src/source/source_cache.test.ts +++ b/src/source/source_cache.test.ts @@ -110,7 +110,7 @@ describe('SourceCache#addTile', () => { const sourceCache = createSourceCache(); const spy = vi.fn(); sourceCache._source.loadTile = spy; - + sourceCache.onAdd(undefined); sourceCache._addTile(tileID); expect(spy).toHaveBeenCalledTimes(1); @@ -594,7 +594,7 @@ describe('SourceCache#update', () => { hasTile: (coord) => (coord.canonical.x !== 0) }); const dataPromise = waitForEvent(sourceCache, 'data', e => e.sourceDataType === 'metadata'); - + sourceCache.onAdd(undefined); await dataPromise; sourceCache.update(transform); @@ -1805,16 +1805,16 @@ describe('SourceCache#tilesIn', () => { transform.resize(512, 512); transform.setZoom(1.05); transform.setCenter(new LngLat(-179.9, 0.1)); - + const sourceCache = createSourceCache(); sourceCache._source.loadTile = async (tile) => { tile.state = 'loaded'; }; - + const dataPromise = waitForEvent(sourceCache, 'data', e => e.sourceDataType === 'metadata'); sourceCache.onAdd(undefined); await dataPromise; - + sourceCache.update(transform); expect(sourceCache.tilesIn([ @@ -2216,6 +2216,39 @@ describe('SourceCache#findLoadedSibling', () => { expect(sourceCache.findLoadedSibling(notLoadedTiles[2])).toBeNull(); expect(sourceCache.findLoadedSibling(notLoadedTiles[3])).toBeNull(); }); + + test('returns a loaded sibling tile with same z/x/y but different wrap', () => { + const sourceCache = createSourceCache({}); + sourceCache.onAdd(undefined); + const tr = new MercatorTransform(); + tr.resize(512, 512); + sourceCache.updateCacheSize(tr); + + // Add a tile at z=2, x=1, y=0, wrap=0 + const tile0 = { + tileID: new OverscaledTileID(2, 0, 2, 1, 0), + hasData() { return true; } + } as any as Tile; + + // Add a sibling tile at z=2, x=1, y=0, wrap=1 + const tile1 = { + tileID: new OverscaledTileID(2, 1, 2, 1, 0), + hasData() { return true; } + } as any as Tile; + + sourceCache.getTiles()[tile0.tileID.key] = tile0; + sourceCache.getTiles()[tile1.tileID.key] = tile1; + + // Should find tile1 as a sibling of tile0 + expect(sourceCache.findLoadedSibling(tile0.tileID)).toBe(tile1); + + // Should find tile0 as a sibling of tile1 + expect(sourceCache.findLoadedSibling(tile1.tileID)).toBe(tile0); + + // Should return null when no sibling exists (z=2, x=2, y=0, wrap=0) + const tile2 = new OverscaledTileID(2, 0, 2, 2, 0); + expect(sourceCache.findLoadedSibling(tile2)).toBeNull(); + }); }); describe('SourceCache#reload', () => { @@ -2372,7 +2405,7 @@ describe('SourceCache#usedForTerrain', () => { }); }); - + describe('SourceCache::refreshTiles', () => { test('calls reloadTile when tile exists', async () => { const coord = new OverscaledTileID(1, 0, 1, 0, 1); @@ -2389,7 +2422,7 @@ describe('SourceCache::refreshTiles', () => { expect(spy).toHaveBeenCalledOnce(); expect(spy.mock.calls[0][1]).toBe('expired'); }); - + test('does not call reloadTile when tile does not exist', async () => { const coord = new OverscaledTileID(1, 0, 1, 1, 1); const sourceCache = createSourceCache(); diff --git a/src/source/source_cache.ts b/src/source/source_cache.ts index a1d3f5e08af..ceb247afdff 100644 --- a/src/source/source_cache.ts +++ b/src/source/source_cache.ts @@ -431,9 +431,24 @@ export class SourceCache extends Evented { /** * Find a loaded sibling of the given tile */ - findLoadedSibling(tileID: OverscaledTileID): Tile { - // If a tile with this ID already exists, return it - return this._getLoadedTile(tileID); + findLoadedSibling(tileID: OverscaledTileID): Tile | null { + // Sibling tiles: same z/x/y, different wrap + for (const key in this._tiles) { + const candidate = this._tiles[key]; + if ( + candidate.tileID.canonical.z === tileID.canonical.z && + candidate.tileID.canonical.x === tileID.canonical.x && + candidate.tileID.canonical.y === tileID.canonical.y && + candidate.tileID.wrap !== tileID.wrap + ) { + // Only return if the candidate is loaded + const loadedCandidate = this._getLoadedTile(candidate.tileID); + if (loadedCandidate) { + return loadedCandidate; + } + } + } + return null; } _getLoadedTile(tileID: OverscaledTileID): Tile { @@ -1042,7 +1057,7 @@ export class SourceCache extends Evented { bounds.shrinkBy(Math.min(bounds.width(), bounds.height()) * 0.001); const projected = bounds.map(project); - const newBounds = Bounds.fromPoints(transformed); + const newBounds = Bounds.fromPoints(transformed); if (!newBounds.covers(projected)) { transformed = transformed.map((coord) => coord.x > 0.5 ?