mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
Merge pull request #935 from daikaginza/fix/tile-origin-follows-tilejson
fix(tiles): derive the tile origin from the configured TileJSON (#933)
This commit is contained in:
6 files changed
+115
-3
No files matched your search
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
- Vector tile sources take their allowed tile origin from the configured
|
||||||
|
`tileJsonUrl`, so repointing only that URL at a mirror loads tiles from the
|
||||||
|
mirror or fails visibly instead of silently using OpenFreeMap. Thanks to
|
||||||
|
[daikaginza](https://github.com/daikaginza) (#935).
|
||||||
|
|
||||||
- MCP setup examples use the app's default port, `4173`. Thanks to
|
- MCP setup examples use the app's default port, `4173`. Thanks to
|
||||||
[Huanyi Xie](https://github.com/xiehuanyi) (#934) and
|
[Huanyi Xie](https://github.com/xiehuanyi) (#934) and
|
||||||
[shirunjies8-png](https://github.com/shirunjies8-png) (#959) for the same fix.
|
[shirunjies8-png](https://github.com/shirunjies8-png) (#959) for the same fix.
|
||||||
|
|||||||
@@ -4704,6 +4704,10 @@ easier to meet (detection is now on more often), but does not create it.
|
|||||||
shown dot against the allowed source classes and records phase timings.
|
shown dot against the allowed source classes and records phase timings.
|
||||||
Positions and segment distances are precomputed, with no new animation-loop
|
Positions and segment distances are precomputed, with no new animation-loop
|
||||||
allocation. Visible-globe roads use cached terrain without offscreen mesh picks. The road-source label names the geometry being drawn, with partial/unavailable states shown plainly.
|
allocation. Visible-globe roads use cached terrain without offscreen mesh picks. The road-source label names the geometry being drawn, with partial/unavailable states shown plainly.
|
||||||
|
- Vector tile sources accept tiles only from an allowed origin, which defaults
|
||||||
|
to the configured `tileJsonUrl` origin; an explicit `allowedOrigin` overrides
|
||||||
|
it. Repointing only `tileJsonUrl` loads tiles from that host or fails with an
|
||||||
|
origin error rather than silently falling back to OpenFreeMap.
|
||||||
- TileJSON caches successful metadata. Transient failures retry after a
|
- TileJSON caches successful metadata. Transient failures retry after a
|
||||||
five-second cooldown; invalid metadata/origins stay unavailable until the
|
five-second cooldown; invalid metadata/origins stay unavailable until the
|
||||||
source is cleared. Clear resets metadata and cancels pending requests.
|
source is cleared. Clear resets metadata and cancels pending requests.
|
||||||
|
|||||||
@@ -214,11 +214,18 @@ function decodeLayers(bytes, z, x, y, layerNames) {
|
|||||||
return { roads, military };
|
return { roads, military };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Construct an immutable-version road/military tile source without starting I/O. */
|
/**
|
||||||
|
* Construct an immutable-version road/military tile source without starting I/O.
|
||||||
|
*
|
||||||
|
* `allowedOrigin` is deliberately not set here: `createVectorTileSource`
|
||||||
|
* derives it from whichever `tileJsonUrl` is in effect, so a caller that
|
||||||
|
* repoints only `tileJsonUrl` gets tiles from that host or a named error —
|
||||||
|
* never a silent fall back to the default host (#933). Passing
|
||||||
|
* `allowedOrigin` explicitly still overrides both.
|
||||||
|
*/
|
||||||
export function createOpenFreeMapSource(options = {}) {
|
export function createOpenFreeMapSource(options = {}) {
|
||||||
return createVectorTileSource({
|
return createVectorTileSource({
|
||||||
tileJsonUrl: 'https://tiles.openfreemap.org/planet',
|
tileJsonUrl: 'https://tiles.openfreemap.org/planet',
|
||||||
allowedOrigin: 'https://tiles.openfreemap.org',
|
|
||||||
decode: decodeOpenFreeMapTile,
|
decode: decodeOpenFreeMapTile,
|
||||||
maxEntries: 192,
|
maxEntries: 192,
|
||||||
maxCacheBytes: 64 * 1024 * 1024,
|
maxCacheBytes: 64 * 1024 * 1024,
|
||||||
|
|||||||
@@ -320,3 +320,41 @@ test('Capitol, greenbelt and commercial property fixtures exclude non-public dri
|
|||||||
assert.ok(records.some((r) => r.area === area && !r.drivable));
|
assert.ok(records.some((r) => r.area === area && !r.drivable));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('repointing tileJsonUrl at a mirror never falls back to the default tile host', async () => {
|
||||||
|
const urls = [];
|
||||||
|
const mirror = 'http://127.0.0.1:12348';
|
||||||
|
const source = createOpenFreeMapSource({
|
||||||
|
fetchImpl: async (url) => {
|
||||||
|
urls.push(url);
|
||||||
|
return url.endsWith('/planet')
|
||||||
|
? Response.json({ tiles: [`${mirror}/planet/v/{z}/{x}/{y}.pbf`] })
|
||||||
|
: new Response(fixture('ofm-austin-14-3743-6745.pbf'));
|
||||||
|
},
|
||||||
|
tileJsonUrl: `${mirror}/planet`,
|
||||||
|
});
|
||||||
|
await source.fetchBounds(
|
||||||
|
{ south: 30.264, north: 30.271, west: -97.75, east: -97.74 },
|
||||||
|
{ zoom: 14 },
|
||||||
|
);
|
||||||
|
assert.ok(urls.length > 1, 'tiles were fetched');
|
||||||
|
assert.deepEqual(
|
||||||
|
urls.filter((url) => !url.startsWith(mirror)),
|
||||||
|
[],
|
||||||
|
'a repointed source must not fetch from tiles.openfreemap.org',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a mirror that serves the default host TileJSON verbatim fails loudly', async () => {
|
||||||
|
// A transparent caching proxy returns upstream's TileJSON unchanged, whose
|
||||||
|
// tiles[0] is an absolute upstream URL. That must be an error, not 100% of
|
||||||
|
// tile traffic quietly leaving the configured mirror.
|
||||||
|
const source = createOpenFreeMapSource({
|
||||||
|
fetchImpl: async () =>
|
||||||
|
Response.json({
|
||||||
|
tiles: ['https://tiles.openfreemap.org/planet/v/{z}/{x}/{y}.pbf'],
|
||||||
|
}),
|
||||||
|
tileJsonUrl: 'http://127.0.0.1:12348/planet',
|
||||||
|
});
|
||||||
|
await assert.rejects(source.getMetadata(), { retryable: false });
|
||||||
|
});
|
||||||
@@ -16,11 +16,27 @@ export function validTileBounds(box) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The origin a configured TileJSON is served from, or undefined when there is
|
||||||
|
* no absolute URL to read one off. Deriving `allowedOrigin` from it means
|
||||||
|
* repointing `tileJsonUrl` at a mirror cannot silently keep loading tiles from
|
||||||
|
* the old host: the origin check below then names the mismatch instead of
|
||||||
|
* passing it (#933).
|
||||||
|
*/
|
||||||
|
function tileJsonOrigin(tileJsonUrl) {
|
||||||
|
if (typeof tileJsonUrl !== 'string') return undefined;
|
||||||
|
try {
|
||||||
|
return new URL(tileJsonUrl).origin;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** A decoded XYZ cache with bounded workers, reads, retained bytes and request lifetimes. */
|
/** A decoded XYZ cache with bounded workers, reads, retained bytes and request lifetimes. */
|
||||||
export function createVectorTileSource({
|
export function createVectorTileSource({
|
||||||
tileJsonUrl,
|
tileJsonUrl,
|
||||||
template,
|
template,
|
||||||
allowedOrigin,
|
allowedOrigin = tileJsonOrigin(tileJsonUrl),
|
||||||
decode,
|
decode,
|
||||||
fetchImpl = (...args) => globalThis.fetch(...args),
|
fetchImpl = (...args) => globalThis.fetch(...args),
|
||||||
maxTiles = 16,
|
maxTiles = 16,
|
||||||
|
|||||||
@@ -203,3 +203,45 @@ test('polygon-selected tiles keep nearest-first order and cached revisits issue
|
|||||||
'over-budget selection is rejected before fetching',
|
'over-budget selection is rejected before fetching',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a configured tileJsonUrl sets the tile origin, and a foreign tiles[0] is still refused', async () => {
|
||||||
|
// Without an explicit allowedOrigin the mirror's own TileJSON is honoured…
|
||||||
|
const mirrored = createVectorTileSource({
|
||||||
|
tileJsonUrl: 'http://127.0.0.1:12348/planet',
|
||||||
|
decode: () => [],
|
||||||
|
fetchImpl: async () =>
|
||||||
|
Response.json({ tiles: ['http://127.0.0.1:12348/p/{z}/{x}/{y}.pbf'] }),
|
||||||
|
});
|
||||||
|
assert.equal(
|
||||||
|
(await mirrored.getMetadata()).template,
|
||||||
|
'http://127.0.0.1:12348/p/{z}/{x}/{y}.pbf',
|
||||||
|
);
|
||||||
|
|
||||||
|
// …while a TileJSON that names a different host is refused rather than
|
||||||
|
// silently fetched, which is what the origin check is for.
|
||||||
|
const foreign = createVectorTileSource({
|
||||||
|
tileJsonUrl: 'http://127.0.0.1:12348/planet',
|
||||||
|
decode: () => [],
|
||||||
|
fetchImpl: async () =>
|
||||||
|
Response.json({
|
||||||
|
tiles: ['https://tiles.example/planet/{z}/{x}/{y}.pbf'],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
await assert.rejects(foreign.getMetadata(), { retryable: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('an explicit allowedOrigin still wins over the one derived from tileJsonUrl', async () => {
|
||||||
|
const source = createVectorTileSource({
|
||||||
|
tileJsonUrl: 'http://127.0.0.1:12348/planet',
|
||||||
|
allowedOrigin: 'https://tiles.example',
|
||||||
|
decode: () => [],
|
||||||
|
fetchImpl: async () =>
|
||||||
|
Response.json({
|
||||||
|
tiles: ['https://tiles.example/planet/{z}/{x}/{y}.pbf'],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
assert.equal(
|
||||||
|
(await source.getMetadata()).template,
|
||||||
|
'https://tiles.example/planet/{z}/{x}/{y}.pbf',
|
||||||
|
);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user