98 lines
2.9 KiB
JavaScript
98 lines
2.9 KiB
JavaScript
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const SOURCE_URL = "https://raw.githubusercontent.com/zhChuXiao/ChinaGeoJson/master/info.json";
|
|
const outputPath = resolve(
|
|
dirname(fileURLToPath(import.meta.url)),
|
|
"../src/data/china-locations.json",
|
|
);
|
|
|
|
const response = await fetch(SOURCE_URL, {
|
|
headers: { "user-agent": "Jyotisha location-data refresh" },
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Unable to download China location data: ${response.status} ${response.statusText}`);
|
|
}
|
|
|
|
const source = await response.json();
|
|
|
|
function childrenOf(code) {
|
|
const record = source[String(code)];
|
|
return Array.isArray(record?.children) ? record.children : [];
|
|
}
|
|
|
|
function toLocation(node) {
|
|
const [longitude, latitude] = Array.isArray(node.center) ? node.center : [];
|
|
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) {
|
|
throw new Error(`Missing valid coordinates for ${node.name} (${node.adcode})`);
|
|
}
|
|
|
|
return {
|
|
code: String(node.adcode),
|
|
name: node.name,
|
|
center: [longitude, latitude],
|
|
};
|
|
}
|
|
|
|
function toCity(node) {
|
|
const location = toLocation(node);
|
|
return {
|
|
...location,
|
|
districts: childrenOf(node.adcode).map(toLocation),
|
|
};
|
|
}
|
|
|
|
function toProvince(node) {
|
|
const location = toLocation(node);
|
|
const directChildren = childrenOf(node.adcode);
|
|
const directDistricts = directChildren.filter((child) => child.level === "district");
|
|
|
|
// Direct-administered municipalities, Hong Kong and Macao have districts
|
|
// directly below the province-level node. A virtual city keeps the UI's
|
|
// country → province → city → district sequence consistent.
|
|
if (directChildren.length === directDistricts.length) {
|
|
return {
|
|
...location,
|
|
cities: [{
|
|
code: `${node.adcode}-city`,
|
|
name: node.name,
|
|
center: location.center,
|
|
districts: directDistricts.map(toLocation),
|
|
}],
|
|
};
|
|
}
|
|
|
|
return {
|
|
...location,
|
|
cities: directChildren.map(toCity),
|
|
};
|
|
}
|
|
|
|
const countryChildren = childrenOf(100000).filter((node) => node.level === "province");
|
|
const output = {
|
|
source: {
|
|
name: "ChinaGeoJson / DataV.GeoAtlas",
|
|
url: SOURCE_URL,
|
|
license: "MIT",
|
|
generatedAt: new Date().toISOString(),
|
|
},
|
|
country: {
|
|
code: "CN",
|
|
name: "中国",
|
|
timezone: 8,
|
|
provinces: countryChildren.map(toProvince),
|
|
},
|
|
};
|
|
|
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
await writeFile(outputPath, `${JSON.stringify(output)}\n`, "utf8");
|
|
|
|
const cityCount = output.country.provinces.reduce((total, province) => total + province.cities.length, 0);
|
|
const districtCount = output.country.provinces.reduce(
|
|
(total, province) => total + province.cities.reduce((sum, city) => sum + city.districts.length, 0),
|
|
0,
|
|
);
|
|
console.log(`Wrote ${outputPath}: ${output.country.provinces.length} provinces, ${cityCount} cities, ${districtCount} districts.`);
|