diff --git a/server/src/db/migrations.ts b/server/src/db/migrations.ts index 63eeb7b8..a05cd371 100644 --- a/server/src/db/migrations.ts +++ b/server/src/db/migrations.ts @@ -2237,6 +2237,34 @@ function runMigrations(db: Database.Database): void { try { db.exec('ALTER TABLE oauth_clients ADD COLUMN allows_client_credentials INTEGER NOT NULL DEFAULT 0'); } catch (err: any) { if (!err.message?.includes('duplicate column name')) throw err; } }, + // Drop stale atlas cache rows for territories that used to resolve to their + // surrounding country (Hong Kong/Macau as China, San Marino/Vatican as Italy, + // etc.) before their own bounding boxes existed. The next atlas stats request + // re-resolves any place inside these boxes with the corrected country code. + () => { + const enclaveBoxes: [number, number, number, number][] = [ + [113.83, 22.15, 114.43, 22.56], // HK + [113.53, 22.10, 113.60, 22.21], // MO + [12.40, 43.89, 12.52, 43.99], // SM + [12.44, 41.90, 12.46, 41.91], // VA + [7.40, 43.72, 7.44, 43.75], // MC + [9.47, 47.05, 9.64, 47.27], // LI + [-5.36, 36.11, -5.33, 36.16], // GI + [-67.30, 17.88, -65.22, 18.53], // PR + ]; + try { + const del = db.prepare( + `DELETE FROM place_regions WHERE place_id IN ( + SELECT id FROM places WHERE lat BETWEEN ? AND ? AND lng BETWEEN ? AND ? + )` + ); + for (const [minLng, minLat, maxLng, maxLat] of enclaveBoxes) { + del.run(minLat, maxLat, minLng, maxLng); + } + } catch (err: any) { + if (!err.message?.includes('no such table')) throw err; + } + }, ]; if (currentVersion < migrations.length) { diff --git a/server/src/services/atlasService.ts b/server/src/services/atlasService.ts index 95f70d27..4d26a7d5 100644 --- a/server/src/services/atlasService.ts +++ b/server/src/services/atlasService.ts @@ -100,6 +100,12 @@ export const COUNTRY_BOXES: Record = { 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], + // Territories with their own ISO code that sit inside a larger country's box. + // Listed so getCountryFromCoords()'s smallest-box match picks them over the host + // (e.g. Hong Kong/Macau over China, San Marino/Vatican over Italy). + HK:[113.83,22.15,114.43,22.56],MO:[113.53,22.10,113.60,22.21],SM:[12.40,43.89,12.52,43.99], + VA:[12.44,41.90,12.46,41.91],MC:[7.40,43.72,7.44,43.75],LI:[9.47,47.05,9.64,47.27], + GI:[-5.36,36.11,-5.33,36.16],PR:[-67.30,17.88,-65.22,18.53], }; export const NAME_TO_CODE: Record = { @@ -144,6 +150,9 @@ export const NAME_TO_CODE: Record = { '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', + 'hong kong':'HK','hong kong sar':'HK','macau':'MO','macao':'MO','macau sar':'MO', + 'san marino':'SM','vatican':'VA','vatican city':'VA','holy see':'VA','monaco':'MC', + 'liechtenstein':'LI','gibraltar':'GI','puerto rico':'PR', }; export const CONTINENT_MAP: Record = { @@ -167,6 +176,7 @@ export const CONTINENT_MAP: Record = { 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', + HK:'Asia',MO:'Asia',SM:'Europe',VA:'Europe',MC:'Europe',LI:'Europe',GI:'Europe',PR:'North America', }; // ── Geocoding helpers ───────────────────────────────────────────────────────