@@ -10,6 +10,7 @@ export class MapManager {
1010 private layerMap : Map < string , L . Layer > = new Map ( ) ;
1111 private svgOverlay : SVGSVGElement | null = null ;
1212 private flagPatterns : Map < string , string > = new Map ( ) ;
13+ private flagImages : Map < string , HTMLImageElement > = new Map ( ) ;
1314
1415 constructor ( containerId : string , state : AppState ) {
1516 this . state = state ;
@@ -111,7 +112,10 @@ export class MapManager {
111112 resolve ( ) ;
112113 } ;
113114
114- // Also patch the _drawPath method to handle multiple parts
115+ // Store reference to MapManager for accessing flag images
116+ const mapManager = this ;
117+
118+ // Also patch the _drawPath method to handle multiple parts and flag patterns
115119 const originalDrawPath = bigImageControl . _drawPath . bind ( bigImageControl ) ;
116120 bigImageControl . _drawPath = function ( value : any ) {
117121 if ( value . multiParts ) {
@@ -123,6 +127,46 @@ export class MapManager {
123127 this . ctx [ count ++ ? 'lineTo' : 'moveTo' ] ( point . x , point . y ) ;
124128 } ) ;
125129 if ( partData . closed ) this . ctx . closePath ( ) ;
130+
131+ // Check if this is a flag pattern (URL reference)
132+ if ( value . options . fillColor && typeof value . options . fillColor === 'string' && value . options . fillColor . startsWith ( 'url(#flag-pattern-' ) ) {
133+ // Extract flag code from pattern ID
134+ const match = value . options . fillColor . match ( / f l a g - p a t t e r n - ( [ a - z ] { 2 } ) / ) ;
135+ if ( match ) {
136+ const flagCode = match [ 1 ] . toUpperCase ( ) ;
137+ const img = mapManager . flagImages . get ( flagCode ) ;
138+
139+ if ( img && img . complete && img . naturalWidth > 0 ) {
140+ // Image is loaded, create pattern
141+ try {
142+ const pattern = this . ctx . createPattern ( img , 'repeat' ) ;
143+ if ( pattern ) {
144+ this . ctx . fillStyle = pattern ;
145+ this . ctx . globalAlpha = value . options . fillOpacity || 0.9 ;
146+ this . ctx . fill ( ) ;
147+ this . ctx . globalAlpha = 1 ;
148+
149+ if ( value . options . stroke ) {
150+ this . ctx . strokeStyle = value . options . color || '#ffffff' ;
151+ this . ctx . lineWidth = value . options . weight || 1 ;
152+ this . ctx . stroke ( ) ;
153+ }
154+ return ; // Don't call _feelPath since we handled it
155+ }
156+ } catch ( e ) {
157+ console . warn ( 'Failed to create flag pattern for export:' , e ) ;
158+ }
159+ }
160+
161+ // Fallback to gray if image not loaded or failed
162+ this . ctx . fillStyle = '#d3d3d3' ;
163+ this . ctx . globalAlpha = 0.7 ;
164+ this . ctx . fill ( ) ;
165+ this . ctx . globalAlpha = 1 ;
166+ return ;
167+ }
168+ }
169+
126170 this . _feelPath ( value . options ) ;
127171 } ) ;
128172 } else {
@@ -190,6 +234,14 @@ export class MapManager {
190234 return this . flagPatterns . get ( flagCode ) ! ;
191235 }
192236
237+ // Pre-load flag image for export (if not already loaded)
238+ if ( ! this . flagImages . has ( flagCode ) ) {
239+ const img = new Image ( ) ;
240+ img . crossOrigin = 'anonymous' ;
241+ img . src = `https://flagcdn.com/w160/${ flagCode . toLowerCase ( ) } .png` ;
242+ this . flagImages . set ( flagCode , img ) ;
243+ }
244+
193245 // Find or create SVG element with defs
194246 const mapContainer = this . map . getContainer ( ) ;
195247 const panes = ( this . map as any ) . _panes ;
@@ -437,42 +489,70 @@ export class MapManager {
437489 } ) ;
438490 }
439491
492+ private async waitForFlagImagesToLoad ( ) : Promise < void > {
493+ const promises : Promise < void > [ ] = [ ] ;
494+
495+ this . flagImages . forEach ( ( img , flagCode ) => {
496+ if ( ! img . complete ) {
497+ promises . push (
498+ new Promise ( ( resolve ) => {
499+ if ( img . complete ) {
500+ resolve ( ) ;
501+ } else {
502+ img . onload = ( ) => resolve ( ) ;
503+ img . onerror = ( ) => resolve ( ) ; // Resolve even on error to not block export
504+ }
505+ } )
506+ ) ;
507+ }
508+ } ) ;
509+
510+ if ( promises . length > 0 ) {
511+ await Promise . all ( promises ) ;
512+ // Small additional delay to ensure everything is ready
513+ await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ;
514+ }
515+ }
516+
440517 prepareForExport ( ) : void {
441518 // Close all tooltips
442519 this . closeAllTooltips ( ) ;
443520
444- // Refresh all layer styles to ensure they're captured correctly
445- this . layerMap . forEach ( ( layer , countryId ) => {
446- if ( layer instanceof L . Path ) {
447- const country = this . state . getCountry ( countryId ) ;
448- if ( country ) {
449- const color = this . state . getCountryColor ( countryId ) ;
450- const flag = this . state . getCountryFlag ( countryId ) ;
451-
452- let fillColor = color || '#d3d3d3' ;
453- let fillOpacity = 0.7 ;
454-
455- // Use flag pattern if flag is set
456- if ( flag ) {
457- const patternId = this . createFlagPattern ( flag ) ;
458- fillColor = `url(#${ patternId } )` ;
459- fillOpacity = 0.9 ;
460- }
521+ // Wait for flag images to load before proceeding
522+ this . waitForFlagImagesToLoad ( ) . then ( ( ) => {
523+ // Refresh all layer styles to ensure they're captured correctly
524+ this . layerMap . forEach ( ( layer , countryId ) => {
525+ if ( layer instanceof L . Path ) {
526+ const country = this . state . getCountry ( countryId ) ;
527+ if ( country ) {
528+ const color = this . state . getCountryColor ( countryId ) ;
529+ const flag = this . state . getCountryFlag ( countryId ) ;
530+
531+ let fillColor = color || '#d3d3d3' ;
532+ let fillOpacity = 0.7 ;
533+
534+ // Use flag pattern if flag is set
535+ if ( flag ) {
536+ const patternId = this . createFlagPattern ( flag ) ;
537+ fillColor = `url(#${ patternId } )` ;
538+ fillOpacity = 0.9 ;
539+ }
461540
462- const style = {
463- fill : true ,
464- fillColor : fillColor ,
465- fillOpacity : fillOpacity ,
466- stroke : true ,
467- color : '#ffffff' ,
468- weight : 1 ,
469- opacity : 1 ,
470- } ;
471- layer . setStyle ( style ) ;
472- // Ensure the options are also set
473- layer . options = { ...layer . options , ...style } ;
541+ const style = {
542+ fill : true ,
543+ fillColor : fillColor ,
544+ fillOpacity : fillOpacity ,
545+ stroke : true ,
546+ color : '#ffffff' ,
547+ weight : 1 ,
548+ opacity : 1 ,
549+ } ;
550+ layer . setStyle ( style ) ;
551+ // Ensure the options are also set
552+ layer . options = { ...layer . options , ...style } ;
553+ }
474554 }
475- }
555+ } ) ;
476556 } ) ;
477557 }
478558
0 commit comments