fix(atlas): correct Guangdong Province name in region bundle

This commit is contained in:
jelly
2026-09-07 18:16:05 +08:00
committed by Maurice
parent 5ac6ce40be
commit 8be594d34c
5 changed files with 58 additions and 1 deletions
Binary file not shown.
+5 -1
View File
@@ -180,7 +180,11 @@ function normalizeAdm1(geo, a3, countryName) {
return code return code
} }
return geo.features.map(f => { return geo.features.map(f => {
const name = f.properties?.shapeName || '' let name = f.properties?.shapeName || ''
// geoBoundaries erroneously names Guangdong Province as "Guangzhou Province" (Guangzhou is the capital city).
if (a3 === 'CHN' && name === 'Guangzhou Province') {
name = 'Guangdong Province'
}
const geometry = quantizeGeometry(f.geometry, ADM1_DECIMALS) const geometry = quantizeGeometry(f.geometry, ADM1_DECIMALS)
if (!geometry) return null if (!geometry) return null
// shapeISO is a real ISO 3166-2 code for most features, but geoBoundaries sometimes // shapeISO is a real ISO 3166-2 code for most features, but geoBoundaries sometimes
+41
View File
@@ -4374,6 +4374,47 @@ function runMigrations(db: Database.Database): void {
`); `);
db.exec('CREATE INDEX IF NOT EXISTS idx_roadtrip_day_tracks_place ON roadtrip_day_tracks(place_id)'); db.exec('CREATE INDEX IF NOT EXISTS idx_roadtrip_day_tracks_place ON roadtrip_day_tracks(place_id)');
}, },
/**
* Rename the misnamed Guangdong Province (shipped as "Guangzhou Province").
*
* geoBoundaries labelled the whole province with the name of its capital, so
* every row a user collected under it carries the wrong code. All three
* tables that key on a region are moved over: the two per-user ones with an
* UPDATE OR IGNORE plus a DELETE, because a user who already holds the
* correct region would otherwise hit the unique index and keep a duplicate,
* and place_regions with a plain UPDATE, because place_id is its primary key
* and nothing there can collide.
*
* Appended LAST: the array is index-addressed against schema_version.
*/
() => {
db.prepare(
`UPDATE OR IGNORE visited_regions
SET region_code = 'CN-GUANGDONGPROVINCE', region_name = 'Guangdong Province'
WHERE UPPER(country_code) = 'CN' AND (region_code = 'CN-GUANGZHOUPROVINCE' OR region_name = 'Guangzhou Province')`,
).run();
db.prepare(
`DELETE FROM visited_regions
WHERE UPPER(country_code) = 'CN' AND (region_code = 'CN-GUANGZHOUPROVINCE' OR region_name = 'Guangzhou Province')`,
).run();
db.prepare(
`UPDATE OR IGNORE place_regions
SET region_code = 'CN-GUANGDONGPROVINCE', region_name = 'Guangdong Province'
WHERE UPPER(country_code) = 'CN' AND (region_code = 'CN-GUANGZHOUPROVINCE' OR region_name = 'Guangzhou Province')`,
).run();
// hidden_regions is the other direction: it remembers which derived region
// a user switched off. Left behind, the tombstone stops matching and the
// region a user deliberately hid comes back.
db.prepare(
`UPDATE OR IGNORE hidden_regions
SET region_code = 'CN-GUANGDONGPROVINCE'
WHERE UPPER(country_code) = 'CN' AND region_code = 'CN-GUANGZHOUPROVINCE'`,
).run();
db.prepare(
`DELETE FROM hidden_regions
WHERE UPPER(country_code) = 'CN' AND region_code = 'CN-GUANGZHOUPROVINCE'`,
).run();
},
]; ];
if (currentVersion < migrations.length) { if (currentVersion < migrations.length) {
@@ -135,6 +135,8 @@ const ALLOWED_DESTRUCTIVE: Record<string, string> = {
'Atlas enclave fix: DELETE ... WHERE place_id IN (places inside specific enclave boxes) — invalidate stale region cache; re-resolved on next request.', 'Atlas enclave fix: DELETE ... WHERE place_id IN (places inside specific enclave boxes) — invalidate stale region cache; re-resolved on next request.',
'DELETE FROM visited_regions': 'DELETE FROM visited_regions':
'Atlas geoBoundaries swap (#1119): DELETE ... WHERE id = ? — after UPDATE OR IGNORE re-codes a manually-marked region to its current code, drop only the single leftover row whose UNIQUE(user_id, region_code) collision caused the update to be skipped (a duplicate of a region the user already has).', 'Atlas geoBoundaries swap (#1119): DELETE ... WHERE id = ? — after UPDATE OR IGNORE re-codes a manually-marked region to its current code, drop only the single leftover row whose UNIQUE(user_id, region_code) collision caused the update to be skipped (a duplicate of a region the user already has).',
'DELETE FROM hidden_regions':
'Guangdong rename (#2284): DELETE ... WHERE region_code = the retired CN-GUANGZHOUPROVINCE, run right after an UPDATE OR IGNORE re-codes the tombstone. Only a row the update had to skip survives to here, and it can only be skipped because UNIQUE(user_id, region_code) already holds the same tombstone under the correct code, so the user keeps the region hidden either way.',
'DELETE FROM reservation_day_positions': 'DELETE FROM reservation_day_positions':
'DELETE ... WHERE the row joins a reservation and a day sitting on different trips. The table has no trip_id and its two foreign keys only require the ids to exist, so a pair that never belonged together was storable; the writer scopes to the trip now and this clears what earlier builds allowed. Bounded by the join — a row whose reservation and day agree on their trip is untouched.', 'DELETE ... WHERE the row joins a reservation and a day sitting on different trips. The table has no trip_id and its two foreign keys only require the ids to exist, so a pair that never belonged together was storable; the writer scopes to the trip now and this clears what earlier builds allowed. Bounded by the join — a row whose reservation and day agree on their trip is untouched.',
}; };
@@ -78,4 +78,14 @@ describe('Atlas admin1 region bundle (#1217)', () => {
expect(names.has(borough), `${borough} should be a region of its own`).toBe(true); expect(names.has(borough), `${borough} should be a region of its own`).toBe(true);
} }
}); });
it('ATLAS-BUNDLE-006 — China ships Guangdong Province, not Guangzhou Province', () => {
const cn = (features as unknown as { properties: { iso_a2: string | null; name?: string; iso_3166_2: string } }[])
.filter(f => f.properties.iso_a2 === 'CN');
const names = cn.map(f => f.properties.name);
expect(names).toContain('Guangdong Province');
expect(names).not.toContain('Guangzhou Province');
expect(cn.some(f => f.properties.iso_3166_2 === 'CN-GUANGDONGPROVINCE')).toBe(true);
expect(cn.some(f => f.properties.iso_3166_2 === 'CN-GUANGZHOUPROVINCE')).toBe(false);
});
}); });