diff --git a/client/src/pages/AtlasPage.tsx b/client/src/pages/AtlasPage.tsx index 57664ff0..76f40690 100644 --- a/client/src/pages/AtlasPage.tsx +++ b/client/src/pages/AtlasPage.tsx @@ -467,26 +467,31 @@ export default function AtlasPage(): React.ReactElement { if (Object.keys(regionGeoCache.current).length === 0) return - // Build set of visited region codes first + // Build set of visited region codes and per-country name sets const visitedRegionCodes = new Set() - const visitedRegionNames = new Set() + const visitedRegionNamesByCountry = new Map>() const regionPlaceCounts: Record = {} - for (const [, regions] of Object.entries(visitedRegions)) { + for (const [countryCode, regions] of Object.entries(visitedRegions)) { + const names = new Set() for (const r of regions) { visitedRegionCodes.add(r.code) - visitedRegionNames.add(r.name.toLowerCase()) + names.add(r.name.toLowerCase()) regionPlaceCounts[r.code] = r.placeCount - regionPlaceCounts[r.name.toLowerCase()] = r.placeCount + regionPlaceCounts[`${countryCode}:${r.name.toLowerCase()}`] = r.placeCount } + visitedRegionNamesByCountry.set(countryCode, names) } - // Match feature by ISO code OR region name (native or English) + // Match feature by ISO code OR region name scoped to the feature's country const isVisitedFeature = (f: any) => { if (visitedRegionCodes.has(f.properties?.iso_3166_2)) return true + const countryA2 = (f.properties?.iso_a2 || '').toUpperCase() + const countryNames = visitedRegionNamesByCountry.get(countryA2) + if (!countryNames) return false const name = (f.properties?.name || '').toLowerCase() - if (visitedRegionNames.has(name)) return true + if (countryNames.has(name)) return true const nameEn = (f.properties?.name_en || '').toLowerCase() - if (nameEn && visitedRegionNames.has(nameEn)) return true + if (nameEn && countryNames.has(nameEn)) return true return false } @@ -538,7 +543,7 @@ export default function AtlasPage(): React.ReactElement { const regionCode = feature?.properties?.iso_3166_2 || '' const countryA2 = (feature?.properties?.iso_a2 || '').toUpperCase() const visited = isVisitedFeature(feature) - const count = regionPlaceCounts[regionCode] || regionPlaceCounts[regionName.toLowerCase()] || regionPlaceCounts[regionNameEn.toLowerCase()] || 0 + const count = regionPlaceCounts[regionCode] || regionPlaceCounts[`${countryA2}:${regionName.toLowerCase()}`] || regionPlaceCounts[`${countryA2}:${regionNameEn.toLowerCase()}`] || 0 layer.on('click', () => { if (!countryA2) return if (visited) { diff --git a/server/src/services/atlasService.ts b/server/src/services/atlasService.ts index bb48347e..2f38837c 100644 --- a/server/src/services/atlasService.ts +++ b/server/src/services/atlasService.ts @@ -71,18 +71,35 @@ setInterval(() => { export const COUNTRY_BOXES: Record = { AF:[60.5,29.4,75,38.5],AL:[19,39.6,21.1,42.7],DZ:[-8.7,19,12,37.1],AD:[1.4,42.4,1.8,42.7],AO:[11.7,-18.1,24.1,-4.4], AR:[-73.6,-55.1,-53.6,-21.8],AM:[43.4,38.8,46.6,41.3],AU:[112.9,-43.6,153.6,-10.7],AT:[9.5,46.4,17.2,49],AZ:[44.8,38.4,50.4,41.9], - BD:[88.0,20.7,92.7,26.6],BR:[-73.9,-33.8,-34.8,5.3],BE:[2.5,49.5,6.4,51.5],BG:[22.4,41.2,28.6,44.2],CA:[-141,41.7,-52.6,83.1],CL:[-75.6,-55.9,-66.9,-17.5], - CN:[73.6,18.2,134.8,53.6],CO:[-79.1,-4.3,-66.9,12.5],HR:[13.5,42.4,19.5,46.6],CZ:[12.1,48.6,18.9,51.1],DK:[8,54.6,15.2,57.8], - EG:[24.7,22,37,31.7],EE:[21.8,57.5,28.2,59.7],FI:[20.6,59.8,31.6,70.1],FR:[-5.1,41.3,9.6,51.1],DE:[5.9,47.3,15.1,55.1], - GR:[19.4,34.8,29.7,41.8],HU:[16,45.7,22.9,48.6],IS:[-24.5,63.4,-13.5,66.6],IN:[68.2,6.7,97.4,35.5],ID:[95.3,-11,141,5.9], - IR:[44.1,25.1,63.3,39.8],IQ:[38.8,29.1,48.6,37.4],IE:[-10.5,51.4,-6,55.4],IL:[34.3,29.5,35.9,33.3],IT:[6.6,36.6,18.5,47.1], - JP:[129.4,31.1,145.5,45.5],KE:[33.9,-4.7,41.9,5.5],KR:[126,33.2,129.6,38.6],LV:[21,55.7,28.2,58.1],LT:[21,53.9,26.8,56.5], - LU:[5.7,49.4,6.5,50.2],MY:[99.6,0.9,119.3,7.4],MX:[-118.4,14.5,-86.7,32.7],MA:[-13.2,27.7,-1,35.9],NL:[3.4,50.8,7.2,53.5], - NZ:[166.4,-47.3,178.5,-34.4],NO:[4.6,58,31.1,71.2],PK:[60.9,23.7,77.1,37.1],PE:[-81.3,-18.4,-68.7,-0.1],PH:[117,5,126.6,18.5], - PL:[14.1,49,24.1,54.9],PT:[-9.5,36.8,-6.2,42.2],RO:[20.2,43.6,29.7,48.3],RU:[19.6,41.2,180,81.9],SA:[34.6,16.4,55.7,32.2], - RS:[18.8,42.2,23,46.2],SK:[16.8,47.7,22.6,49.6],SI:[13.4,45.4,16.6,46.9],ZA:[16.5,-34.8,32.9,-22.1],ES:[-9.4,36,-0.2,43.8], + BA:[15.7,42.6,19.6,45.3],BD:[88.0,20.7,92.7,26.6],BF:[-5.5,9.4,2.4,15.1],BH:[50.4,25.8,50.7,26.2],BI:[29.0,-4.5,30.8,-2.3], + BJ:[0.8,6.2,3.8,12.4],BN:[114.1,4.0,115.4,5.1],BO:[-69.7,-22.9,-57.5,-9.7],BR:[-73.9,-33.8,-34.8,5.3],BE:[2.5,49.5,6.4,51.5], + BG:[22.4,41.2,28.6,44.2],BW:[20.0,-26.9,29.4,-17.8],CA:[-141,41.7,-52.6,83.1],CD:[12.2,-13.5,31.3,5.4],CG:[11.2,-5.0,18.7,3.7], + CI:[-8.6,4.3,-2.5,10.7],CL:[-75.6,-55.9,-66.9,-17.5],CM:[8.4,1.7,16.2,13.1],CN:[73.6,18.2,134.8,53.6],CO:[-79.1,-4.3,-66.9,12.5], + CR:[-85.9,8.0,-82.5,11.2],CU:[-85.0,19.8,-74.1,23.2],CV:[-25.4,14.8,-22.7,17.2],CY:[32.3,34.5,34.1,35.7],HR:[13.5,42.4,19.5,46.6], + CZ:[12.1,48.6,18.9,51.1],DJ:[41.8,11.0,43.4,12.7],DK:[8,54.6,15.2,57.8],DO:[-72.0,17.5,-68.3,19.9],EC:[-81.0,-5.0,-75.2,1.5], + EG:[24.7,22,37,31.7],EE:[21.8,57.5,28.2,59.7],ER:[36.4,12.4,43.1,18.0],ET:[33.0,3.4,47.9,14.9],FI:[20.6,59.8,31.6,70.1], + FR:[-5.1,41.3,9.6,51.1],DE:[5.9,47.3,15.1,55.1],GE:[40.0,41.0,46.7,43.6],GH:[-3.3,4.7,1.2,11.2],GN:[-15.1,7.2,-7.6,12.7], + GR:[19.4,34.8,29.7,41.8],GT:[-92.2,13.7,-88.2,17.8],HN:[-89.4,12.9,-83.2,16.5],HT:[-74.5,18.0,-71.6,20.1],HU:[16,45.7,22.9,48.6], + IS:[-24.5,63.4,-13.5,66.6],IN:[68.2,6.7,97.4,35.5],ID:[95.3,-11,141,5.9],IR:[44.1,25.1,63.3,39.8],IQ:[38.8,29.1,48.6,37.4], + IE:[-10.5,51.4,-6,55.4],IL:[34.3,29.5,35.9,33.3],IT:[6.6,36.6,18.5,47.1],JM:[-78.4,17.7,-76.2,18.5],JO:[34.9,29.2,39.3,33.4], + JP:[129.4,31.1,145.5,45.5],KE:[33.9,-4.7,41.9,5.5],KG:[69.2,39.2,80.3,43.2],KH:[102.3,10.4,107.6,14.7],KR:[126,33.2,129.6,38.6], + KW:[46.5,28.5,48.4,30.1],KZ:[50.3,40.6,87.4,55.4],LA:[100.1,13.9,107.7,22.5],LB:[35.1,33.1,36.6,34.7],LK:[79.7,5.9,81.9,9.8], + LV:[21,55.7,28.2,58.1],LT:[21,53.9,26.8,56.5],LU:[5.7,49.4,6.5,50.2],LY:[9.5,19.5,25.2,33.3],MA:[-13.2,27.7,-1,35.9], + MD:[26.6,45.5,30.2,48.5],ME:[18.4,41.8,20.4,43.6],MG:[43.2,-25.6,50.5,-11.9],MK:[20.5,40.8,23.0,42.4],ML:[-4.8,10.1,4.3,25.0], + MM:[92.2,9.8,101.2,28.5],MN:[87.8,41.6,119.9,52.1],MR:[-17.1,14.7,-4.8,27.3],MT:[14.1,35.8,14.6,36.1],MU:[57.3,-20.5,57.8,-19.9], + MV:[72.7,-0.7,73.8,7.1],MW:[32.7,-17.1,35.9,-9.4],MY:[99.6,0.9,119.3,7.4],MX:[-118.4,14.5,-86.7,32.7],MZ:[30.2,-26.9,40.8,-10.5], + NA:[11.7,-28.9,25.3,-17.0],NE:[0.2,11.7,15.9,23.5],NI:[-87.7,10.7,-83.1,15.0],NL:[3.4,50.8,7.2,53.5],NP:[80.1,26.4,88.2,30.4], + NZ:[166.4,-47.3,178.5,-34.4],NO:[4.6,58,31.1,71.2],OM:[51.9,16.6,59.8,26.4],PA:[-83.0,7.2,-77.2,9.6],PG:[140.8,-11.7,155.7,-1.3], + PK:[60.9,23.7,77.1,37.1],PE:[-81.3,-18.4,-68.7,-0.1],PH:[117,5,126.6,18.5],PL:[14.1,49,24.1,54.9],PS:[34.2,29.5,35.6,32.6], + PT:[-9.5,36.8,-6.2,42.2],PY:[-62.6,-27.6,-54.3,-19.3],QA:[50.7,24.5,51.6,26.2],RO:[20.2,43.6,29.7,48.3],RU:[19.6,41.2,180,81.9], + RW:[29.0,-2.8,30.9,-1.0],SA:[34.6,16.4,55.7,32.2],SC:[55.3,-9.7,55.8,-3.7],SD:[21.8,3.4,38.6,22.2],SG:[103.6,1.2,104.1,1.5], + SI:[13.4,45.4,16.6,46.9],SK:[16.8,47.7,22.6,49.6],SN:[-17.5,12.3,-11.4,15.0],SO:[40.9,-1.7,51.4,11.9],RS:[18.8,42.2,23,46.2], + SV:[-90.1,13.2,-87.7,14.5],SY:[35.7,32.3,42.4,37.3],TG:[-0.2,6.1,1.8,11.2],TJ:[67.3,36.7,75.2,41.0],TM:[52.4,35.1,66.7,42.8], + TN:[7.5,30.2,11.6,37.5],TT:[-61.9,10.0,-60.5,11.3],TW:[120.1,21.9,122.0,25.3],TZ:[29.3,-11.7,40.4,-1.0],ZA:[16.5,-34.8,32.9,-22.1], SE:[11.1,55.3,24.2,69.1],CH:[6,45.8,10.5,47.8],TH:[97.3,5.6,105.6,20.5],TR:[26,36,44.8,42.1],UA:[22.1,44.4,40.2,52.4], - AE:[51.6,22.6,56.4,26.1],GB:[-8,49.9,2,60.9],US:[-125,24.5,-66.9,49.4],VN:[102.1,8.6,109.5,23.4], + UG:[29.6,-1.5,35.0,4.2],UY:[-58.4,-34.9,-53.1,-30.1],UZ:[55.9,37.2,73.1,45.6],VE:[-73.4,0.7,-59.8,12.2], + AE:[51.6,22.6,56.4,26.1],GB:[-8,49.9,2,60.9],US:[-125,24.5,-66.9,49.4],VN:[102.1,8.6,109.5,23.4],XK:[20.0,41.9,21.8,43.3], + YE:[42.5,12.1,54.0,19.0],ZM:[21.9,-18.1,33.7,-8.2],ZW:[25.2,-22.4,33.1,-15.6], }; export const NAME_TO_CODE: Record = { @@ -106,16 +123,50 @@ export const NAME_TO_CODE: Record = { 'malaysia':'MY','colombia':'CO','kolumbien':'CO','peru':'PE','chile':'CL','iran':'IR', 'iraq':'IQ','irak':'IQ','pakistan':'PK','kenya':'KE','kenia':'KE','nigeria':'NG', 'saudi arabia':'SA','saudi-arabien':'SA','albania':'AL','albanien':'AL', + 'georgia':'GE','georgien':'GE','montenegro':'ME','north macedonia':'MK','nordmazedonien':'MK', + 'macedonia':'MK','bosnia':'BA','bosnia and herzegovina':'BA','bosnien':'BA','kosovo':'XK', + 'cyprus':'CY','zypern':'CY','malta':'MT','tunisia':'TN','tunesien':'TN','jordan':'JO','jordanien':'JO', + 'lebanon':'LB','libanon':'LB','ghana':'GH','ethiopia':'ET','athiopien':'ET','tanzania':'TZ','uganda':'UG', + 'singapore':'SG','taiwan':'TW','nepal':'NP','sri lanka':'LK','cambodia':'KH','kambodscha':'KH', + 'myanmar':'MM','burma':'MM','laos':'LA','mongolia':'MN','mongolei':'MN','kazakhstan':'KZ','kasachstan':'KZ', + 'uzbekistan':'UZ','usbekistan':'UZ','kyrgyzstan':'KG','kirgisistan':'KG','tajikistan':'TJ','tadschikistan':'TJ', + 'turkmenistan':'TM','costa rica':'CR','panama':'PA','ecuador':'EC','uruguay':'UY','cuba':'CU','kuba':'CU', + 'dominican republic':'DO','dominikanische republik':'DO','jamaica':'JM','haiti':'HT','honduras':'HN', + 'guatemala':'GT','el salvador':'SV','nicaragua':'NI','bolivia':'BO','bolivia plurinational state of':'BO', + 'paraguay':'PY','venezuela':'VE','trinidad and tobago':'TT','trinidad':'TT', + 'oman':'OM','kuwait':'KW','qatar':'QA','bahrain':'BH', + 'syria':'SY','syrien':'SY','yemen':'YE','jemen':'YE','palestine':'PS','palastina':'PS', + 'moldova':'MD','republic of moldova':'MD','moldawien':'MD', + 'libya':'LY','libyen':'LY','sudan':'SD','eritrea':'ER','djibouti':'DJ', + 'senegal':'SN','cameroon':'CM','kamerun':'CM','ivory coast':'CI','cote d\'ivoire':'CI', + 'mali':'ML','niger':'NE','burkina faso':'BF','togo':'TG','benin':'BJ','guinea':'GN', + 'dr congo':'CD','democratic republic of the congo':'CD','republic of the congo':'CG','congo':'CG', + 'angola':'AO','namibia':'NA','botswana':'BW','zimbabwe':'ZW','zambia':'ZM','malawi':'MW', + 'mozambique':'MZ','mozambik':'MZ','madagascar':'MG','rwanda':'RW','burundi':'BI', + 'somalia':'SO','papua new guinea':'PG','brunei':'BN', }; export const CONTINENT_MAP: Record = { - AF:'Africa',AL:'Europe',DZ:'Africa',AD:'Europe',AO:'Africa',AR:'South America',AM:'Asia',AU:'Oceania',AT:'Europe',AZ:'Asia', - BR:'South America',BE:'Europe',BG:'Europe',CA:'North America',CL:'South America',CN:'Asia',CO:'South America',HR:'Europe',CZ:'Europe',DK:'Europe', - EG:'Africa',EE:'Europe',FI:'Europe',FR:'Europe',DE:'Europe',GR:'Europe',HU:'Europe',IS:'Europe',IN:'Asia',ID:'Asia', - IR:'Asia',IQ:'Asia',IE:'Europe',IL:'Asia',IT:'Europe',JP:'Asia',KE:'Africa',KR:'Asia',LV:'Europe',LT:'Europe', - LU:'Europe',MY:'Asia',MX:'North America',MA:'Africa',NL:'Europe',NZ:'Oceania',NO:'Europe',PK:'Asia',PE:'South America',PH:'Asia', - PL:'Europe',PT:'Europe',RO:'Europe',RU:'Europe',SA:'Asia',RS:'Europe',SK:'Europe',SI:'Europe',ZA:'Africa',ES:'Europe', - SE:'Europe',CH:'Europe',TH:'Asia',TR:'Europe',UA:'Europe',AE:'Asia',GB:'Europe',US:'North America',VN:'Asia',NG:'Africa', + AF:'Asia',AL:'Europe',DZ:'Africa',AD:'Europe',AO:'Africa',AR:'South America',AM:'Asia',AU:'Oceania',AT:'Europe',AZ:'Asia', + BA:'Europe',BD:'Asia',BF:'Africa',BH:'Asia',BI:'Africa',BJ:'Africa',BN:'Asia',BO:'South America', + BR:'South America',BE:'Europe',BG:'Europe',BW:'Africa', + CA:'North America',CD:'Africa',CG:'Africa',CI:'Africa',CL:'South America',CM:'Africa',CN:'Asia',CO:'South America', + CR:'North America',CU:'North America',CV:'Africa',CY:'Europe',HR:'Europe',CZ:'Europe', + DJ:'Africa',DK:'Europe',DO:'North America',EC:'South America',EG:'Africa',EE:'Europe',ER:'Africa',ET:'Africa', + FI:'Europe',FR:'Europe',DE:'Europe',GE:'Asia',GH:'Africa',GN:'Africa',GR:'Europe',GT:'North America', + HN:'North America',HT:'North America',HU:'Europe',IS:'Europe',IN:'Asia',ID:'Asia',IR:'Asia',IQ:'Asia', + IE:'Europe',IL:'Asia',IT:'Europe',JM:'North America',JO:'Asia',JP:'Asia',KE:'Africa',KG:'Asia',KH:'Asia', + KR:'Asia',KW:'Asia',KZ:'Asia',LA:'Asia',LB:'Asia',LK:'Asia',LV:'Europe',LT:'Europe',LU:'Europe',LY:'Africa', + MA:'Africa',MD:'Europe',ME:'Europe',MG:'Africa',MK:'Europe',ML:'Africa',MM:'Asia',MN:'Asia',MR:'Africa', + MT:'Europe',MU:'Africa',MV:'Asia',MW:'Africa',MY:'Asia',MX:'North America',MZ:'Africa', + NA:'Africa',NE:'Africa',NI:'North America',NL:'Europe',NP:'Asia',NZ:'Oceania',NO:'Europe',OM:'Asia', + PA:'North America',PG:'Oceania',PK:'Asia',PE:'South America',PH:'Asia',PL:'Europe',PS:'Asia', + PT:'Europe',PY:'South America',QA:'Asia',RO:'Europe',RU:'Europe',RW:'Africa',SA:'Asia',SC:'Africa', + SD:'Africa',SG:'Asia',SI:'Europe',SK:'Europe',SN:'Africa',SO:'Africa',RS:'Europe',SV:'North America', + SY:'Asia',TG:'Africa',TJ:'Asia',TM:'Asia',TN:'Africa',TT:'North America',TW:'Asia',TZ:'Africa', + ZA:'Africa',SE:'Europe',CH:'Europe',TH:'Asia',TR:'Europe',UA:'Europe',UG:'Africa',UY:'South America', + UZ:'Asia',VE:'South America',AE:'Asia',GB:'Europe',US:'North America',VN:'Asia',XK:'Europe', + YE:'Asia',ZM:'Africa',ZW:'Africa',NG:'Africa', }; // ── Geocoding helpers ───────────────────────────────────────────────────────