@@ -10,48 +10,63 @@ import { resolveStyle, MAP_STYLES, haversineMeters, formatDistance, type StyleCh
1010import { ddToDms , ddToUtm , encodeGeohash , formatDd , type LatLng } from '@/tools/geo/coord.lib' ;
1111import type { Lang } from '@/i18n/config' ;
1212
13- // Pre-fetch a MapLibre style URL and inline any TileJSON-backed vector sources
14- // so MapLibre never needs to make a separate TileJSON fetch. This works around
15- // Cloudflare edge-caching issues where the style URL returns stale/broken content
16- // at certain CDN nodes. cache:'no-cache' forces revalidation with the origin server.
13+ // Rewrite OFM absolute URLs to go through our same-origin /ofm/ proxy, which
14+ // fetches from tiles.openfreemap.org server-side over Cloudflare's backbone.
15+ // This bypasses the broken Singapore CDN edge that serves corrupt style/tile
16+ // responses to Southeast Asian users.
17+ const ofm = ( u : unknown ) : unknown =>
18+ typeof u === 'string' ? u . replace ( 'https://tiles.openfreemap.org/' , '/ofm/' ) : u ;
19+
20+ // Pre-fetch a MapLibre style URL through our proxy, inline any TileJSON-backed
21+ // vector sources, and rewrite all remaining OFM URLs so every subsequent fetch
22+ // (glyphs, sprites, tiles) also goes through the same-origin proxy.
1723// eslint-disable-next-line @typescript-eslint/no-explicit-any
1824async function fetchResolvedStyle ( url : string ) : Promise < string | Record < string , any > > {
1925 try {
2026 const res = await fetch ( url , { cache : 'no-cache' } ) ;
2127 console . log ( '[map] style fetch' , url , res . status , res . ok ) ;
2228 if ( ! res . ok ) return url ;
2329 // eslint-disable-next-line @typescript-eslint/no-explicit-any
24- const style = await res . json ( ) as { version ?: number ; sources ?: Record < string , any > } ;
25- // Validate: must look like a MapLibre style (has version + sources)
30+ const style = await res . json ( ) as Record < string , any > ;
2631 if ( ! style . version || ! style . sources ) {
2732 console . warn ( '[map] style response invalid (no version/sources):' , JSON . stringify ( style ) . slice ( 0 , 200 ) ) ;
2833 return url ;
2934 }
35+ // Rewrite top-level glyph/sprite URLs through our proxy
36+ if ( typeof style . glyphs === 'string' ) style . glyphs = ofm ( style . glyphs ) ;
37+ if ( typeof style . sprite === 'string' ) style . sprite = ofm ( style . sprite ) ;
38+ // Rewrite sources and inline vector TileJSON
3039 await Promise . all (
40+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3141 Object . values ( style . sources ) . map ( async ( src ) => {
3242 // eslint-disable-next-line @typescript-eslint/no-explicit-any
3343 const s = src as Record < string , any > ;
44+ // Rewrite any existing raster tile URLs
45+ if ( Array . isArray ( s . tiles ) ) s . tiles = s . tiles . map ( ofm ) ;
46+ // Inline vector TileJSON and rewrite tile URLs through proxy
3447 if ( s . type === 'vector' && typeof s . url === 'string' && ! s . tiles ) {
48+ const tjUrl = ofm ( s . url ) as string ;
3549 try {
36- console . log ( '[map] fetching TileJSON' , s . url ) ;
37- const tj = await fetch ( s . url , { cache : 'no-cache' } ) . then ( r => r . json ( ) ) as {
50+ console . log ( '[map] fetching TileJSON' , tjUrl ) ;
51+ const tj = await fetch ( tjUrl , { cache : 'no-cache' } ) . then ( r => r . json ( ) ) as {
3852 tiles ?: string [ ] ; minzoom ?: number ; maxzoom ?: number ;
3953 } ;
4054 console . log ( '[map] TileJSON tiles[0]:' , tj . tiles ?. [ 0 ] , 'maxzoom:' , tj . maxzoom ) ;
4155 if ( tj . tiles ?. length ) {
42- s . tiles = tj . tiles ;
56+ s . tiles = tj . tiles . map ( ofm ) ;
4357 if ( tj . minzoom != null ) s . minzoom = tj . minzoom ;
4458 if ( tj . maxzoom != null ) s . maxzoom = tj . maxzoom ;
4559 delete s . url ;
4660 }
4761 } catch ( e ) {
4862 console . error ( '[map] TileJSON fetch failed:' , e ) ;
63+ s . url = tjUrl ; // leave the proxy URL so MapLibre can retry via proxy
4964 }
5065 }
5166 } )
5267 ) ;
5368 // eslint-disable-next-line @typescript-eslint/no-explicit-any
54- const omt = ( style . sources as Record < string , any > ) ?. openmaptiles ;
69+ const omt = style . sources ?. openmaptiles ;
5570 console . log ( '[map] openmaptiles source after resolve:' , omt ?. tiles ?. [ 0 ] ?? '(still url-based: ' + omt ?. url + ')' ) ;
5671 return style ;
5772 } catch ( e ) {
0 commit comments