mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-11 03:13:56 +00:00
refactor(data): store city boundaries as .json behind a generated barrel
Bundlers do not treat .geojson as a module, so importing a boundary statically failed to resolve. The content is JSON either way, and the extension was the only thing standing in the way of the coverage page reading a city outline. The barrel matches the one the panorama indexes already use: generated from what is on disk, rewritten after each city, so a build that has only produced some of them still compiles.
This commit is contained in:
1 parent
faa7cf987b
commit
adeebc46e2
9 files changed
+59
-5
No files matched your search
@@ -11,7 +11,7 @@
|
||||
// Output lands in src/data/boundaries/<code>.geojson and is meant to be edited
|
||||
// by hand afterwards if a city needs trimming. Re-running overwrites it.
|
||||
|
||||
import { mkdirSync, writeFileSync } from 'node:fs';
|
||||
import { mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
||||
import * as turf from '@turf/turf';
|
||||
|
||||
const NOMINATIM = 'https://nominatim.openstreetmap.org/search';
|
||||
@@ -166,7 +166,7 @@ async function buildCity(code, config) {
|
||||
};
|
||||
|
||||
mkdirSync(OUT_DIR, { recursive: true });
|
||||
const path = `${OUT_DIR}/${code.toLowerCase()}.geojson`;
|
||||
const path = `${OUT_DIR}/${code.toLowerCase()}.json`;
|
||||
writeFileSync(path, JSON.stringify(out, null, 1) + '\n');
|
||||
console.log(
|
||||
` -> ${path} ${out.properties.areaKm2} km2, ` +
|
||||
@@ -174,9 +174,45 @@ async function buildCity(code, config) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite the barrel module that imports every boundary that exists.
|
||||
*
|
||||
* Same reason as the panorama indexes: static imports let the data be bundled
|
||||
* with the server build, but a city that has not been generated yet would then
|
||||
* be a hard build error. Generating the list from disk keeps a partial build
|
||||
* usable.
|
||||
* @returns {string[]} City codes now in the barrel.
|
||||
*/
|
||||
function writeBarrel() {
|
||||
const entries = readdirSync(OUT_DIR)
|
||||
.filter((name) => name.endsWith('.json') && name !== 'index.js')
|
||||
.sort()
|
||||
.map((name) => ({
|
||||
code: name.replace(/\.json$/, '').toUpperCase(),
|
||||
ident: name.replace(/\.json$/, ''),
|
||||
file: name,
|
||||
}));
|
||||
|
||||
const body =
|
||||
'// Generated by scripts/build-city-boundaries.mjs. Do not edit by hand.\n' +
|
||||
'//\n' +
|
||||
'// Lists only the boundaries that have actually been built, so a partial\n' +
|
||||
'// build still compiles.\n\n' +
|
||||
entries.map((e) => `import ${e.ident} from './${e.file}';`).join('\n') +
|
||||
'\n\nexport const CITY_BOUNDARIES = {\n' +
|
||||
entries.map((e) => ` ${e.code}: ${e.ident},`).join('\n') +
|
||||
'\n};\n';
|
||||
|
||||
writeFileSync(`${OUT_DIR}/index.js`, body);
|
||||
return entries.map((e) => e.code);
|
||||
}
|
||||
|
||||
const only = process.argv.slice(2);
|
||||
for (const [code, config] of Object.entries(CITIES)) {
|
||||
if (only.length && !only.includes(code)) continue;
|
||||
await buildCity(code, config);
|
||||
// Rewritten after each city so an interrupted run leaves a consistent barrel.
|
||||
writeBarrel();
|
||||
}
|
||||
console.log('\ndone');
|
||||
console.log(`\nbarrel: ${writeBarrel().join(', ')}`);
|
||||
console.log('done');
|
||||
@@ -155,7 +155,7 @@ function thinToGrid(points) {
|
||||
}
|
||||
|
||||
async function buildCity(code, token) {
|
||||
const boundaryPath = `${BOUNDARY_DIR}/${code.toLowerCase()}.geojson`;
|
||||
const boundaryPath = `${BOUNDARY_DIR}/${code.toLowerCase()}.json`;
|
||||
if (!existsSync(boundaryPath)) {
|
||||
throw new Error(`${code}: missing ${boundaryPath} — run build-city-boundaries.mjs first`);
|
||||
}
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,18 @@
|
||||
// Generated by scripts/build-city-boundaries.mjs. Do not edit by hand.
|
||||
//
|
||||
// Lists only the boundaries that have actually been built, so a partial
|
||||
// build still compiles.
|
||||
|
||||
import dh from './dh.json';
|
||||
import dl from './dl.json';
|
||||
import dn from './dn.json';
|
||||
import hn from './hn.json';
|
||||
import tphcm from './tphcm.json';
|
||||
|
||||
export const CITY_BOUNDARIES = {
|
||||
DH: dh,
|
||||
DL: dl,
|
||||
DN: dn,
|
||||
HN: hn,
|
||||
TPHCM: tphcm,
|
||||
};
|
||||
File renamed without changes.
@@ -43,7 +43,7 @@ describe('panorama index', () => {
|
||||
|
||||
it.each(CODES)('%s panoramas sit inside the city boundary', (code) => {
|
||||
const boundary = JSON.parse(
|
||||
readFileSync(`src/data/boundaries/${code.toLowerCase()}.geojson`, 'utf8')
|
||||
readFileSync(`src/data/boundaries/${code.toLowerCase()}.json`, 'utf8')
|
||||
);
|
||||
// Sample rather than test every point: booleanPointInPolygon against a
|
||||
// detailed outline is slow, and a clipping bug would not hide in a sample.
|
||||
|
||||
Reference in new issue
Block a user