// Yurtdışı üniversite sayfaları için ülke kontürlerini üretir. // // Kaynak: Natural Earth 10m admin_0_countries (kamu malı). GeoJSON bir kez // indirilip tmp'ye önbelleklenir; çıktı src/lib/harita-ulkeler-verisi.ts // dosyasına yazılır ve repo'ya commit'lenir (build ağa çıkmaz). // // Projeksiyon: ülke başına eşdikdörtgen (plate carrée) + cos(ortaEnlem) // düzeltmesi; Türkiye haritasının kırpılmış oranıyla aynı 1050×447 kutuya // sığdırılır. Nokta seyreltme ~1px eşikli olduğundan çıktı küçük kalır. // // Çalıştırma: pnpm tsx scripts/ulke-harita-uret.ts import fs from "node:fs"; import os from "node:os"; import path from "node:path"; const GENISLIK = 1050; const YUKSEKLIK = 447; const KENAR_PAYI = 26; const SEYRELTME_PX = 1.1; const MIN_HALKA_PX = 3; // bbox'u bundan küçük halkalar (adacıklar) atlanır const KAYNAK_URL = "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_10m_admin_0_countries.geojson"; type UlkeTanim = { kod: string; ad: string; /** Vurgulanan (mavi) NE ADMIN adları */ vurgulu: string[]; /** Soluk çizilen bağlam parçaları (ör. Kıbrıs'ın güneyi) */ baglam?: string[]; }; const ULKELER: UlkeTanim[] = [ { kod: "kktc", ad: "KKTC", vurgulu: ["Northern Cyprus"], baglam: [ "Cyprus", "Cyprus No Mans Area", "Dhekelia Sovereign Base Area", "Akrotiri Sovereign Base Area", ], }, { kod: "azerbaycan", ad: "Azerbaycan", vurgulu: ["Azerbaijan"] }, { kod: "kirgizistan", ad: "Kırgızistan", vurgulu: ["Kyrgyzstan"] }, { kod: "kazakistan", ad: "Kazakistan", vurgulu: ["Kazakhstan"] }, { kod: "bosna-hersek", ad: "Bosna-Hersek", vurgulu: ["Bosnia and Herzegovina"], }, { kod: "makedonya", ad: "Kuzey Makedonya", vurgulu: ["North Macedonia"] }, { kod: "arnavutluk", ad: "Arnavutluk", vurgulu: ["Albania"] }, ]; type Halka = [number, number][]; // [lng, lat] async function geojsonGetir(): Promise<{ features: { properties: { ADMIN: string }; geometry: { type: string; coordinates: unknown }; }[]; }> { const cachePath = path.join(os.tmpdir(), "ne_10m_admin_0_countries.geojson"); if (!fs.existsSync(cachePath)) { console.log("indiriliyor:", KAYNAK_URL); const res = await fetch(KAYNAK_URL); if (!res.ok) throw new Error(`indirme başarısız: ${res.status}`); fs.writeFileSync(cachePath, Buffer.from(await res.arrayBuffer())); } return JSON.parse(fs.readFileSync(cachePath, "utf8")); } function halkalar(geometry: { type: string; coordinates: unknown }): Halka[] { if (geometry.type === "Polygon") return geometry.coordinates as Halka[]; if (geometry.type === "MultiPolygon") return (geometry.coordinates as Halka[][]).flat(); throw new Error(`beklenmeyen geometri: ${geometry.type}`); } function pathUret( ringler: Halka[], proje: (lng: number, lat: number) => { x: number; y: number }, ): string[] { const parcalar: string[] = []; for (const ring of ringler) { const pts = ring.map(([lng, lat]) => proje(lng, lat)); const xs = pts.map((p) => p.x); const ys = pts.map((p) => p.y); if ( Math.max(...xs) - Math.min(...xs) < MIN_HALKA_PX && Math.max(...ys) - Math.min(...ys) < MIN_HALKA_PX ) continue; let d = ""; let sonX = NaN; let sonY = NaN; for (let i = 0; i < pts.length; i++) { const { x, y } = pts[i]; if (i > 0 && Math.hypot(x - sonX, y - sonY) < SEYRELTME_PX) continue; d += `${d ? "L" : "M"}${x.toFixed(1)} ${y.toFixed(1)}`; sonX = x; sonY = y; } d += "Z"; if (d.length > 10) parcalar.push(d); } return parcalar; } async function main() { const geojson = await geojsonGetir(); const byAdmin = new Map( geojson.features.map((f) => [f.properties.ADMIN, f] as const), ); const cikti: string[] = []; const kodlar = ULKELER.map((u) => `"${u.kod}"`).join(" | "); cikti.push( "// Bu dosya scripts/ulke-harita-uret.ts tarafından üretildi — elle düzenleme.", "// Kaynak: Natural Earth 10m admin_0_countries (kamu malı).", "", `export type UlkeKodu = ${kodlar};`, "", "export type UlkeHarita = {", " ad: string;", " /** lat/lng → viewBox koordinatı: x = ax*lng + bx, y = ay*lat + by */", " proje: { ax: number; bx: number; ay: number; by: number };", " /** Vurgulanan ülke kontürleri */", " ulke: string[];", " /** Soluk çizilen bağlam kontürleri (ör. Kıbrıs adasının güneyi) */", " baglam: string[];", "};", "", `export const ULKE_HARITA_GENISLIK = ${GENISLIK};`, `export const ULKE_HARITA_YUKSEKLIK = ${YUKSEKLIK};`, "", "export const ULKE_HARITALARI: Record = {", ); for (const ulke of ULKELER) { const adlar = [...ulke.vurgulu, ...(ulke.baglam ?? [])]; const geometriler = adlar.map((ad) => { const f = byAdmin.get(ad); if (!f) throw new Error(`Natural Earth'te bulunamadı: ${ad}`); return { ad, ringler: halkalar(f.geometry) }; }); // Sığdırma: tüm parçaların (bağlam dahil) bbox'una göre tek ölçek const tumNoktalar = geometriler.flatMap((g) => g.ringler.flat()); const latler = tumNoktalar.map(([, lat]) => lat); const ortaLat = (Math.min(...latler) + Math.max(...latler)) / 2; const kx = Math.cos((ortaLat * Math.PI) / 180); const pxler = tumNoktalar.map(([lng]) => lng * kx); const pyler = tumNoktalar.map(([, lat]) => -lat); const minX = Math.min(...pxler); const maxX = Math.max(...pxler); const minY = Math.min(...pyler); const maxY = Math.max(...pyler); const s = Math.min( (GENISLIK - 2 * KENAR_PAYI) / (maxX - minX), (YUKSEKLIK - 2 * KENAR_PAYI) / (maxY - minY), ); const ox = (GENISLIK - s * (maxX - minX)) / 2; const oy = (YUKSEKLIK - s * (maxY - minY)) / 2; const proje = (lng: number, lat: number) => ({ x: s * (lng * kx - minX) + ox, y: s * (-lat - minY) + oy, }); const vurguluPaths = geometriler .filter((g) => ulke.vurgulu.includes(g.ad)) .flatMap((g) => pathUret(g.ringler, proje)); const baglamPaths = geometriler .filter((g) => !ulke.vurgulu.includes(g.ad)) .flatMap((g) => pathUret(g.ringler, proje)); const yaz = (n: number) => Number(n.toFixed(4)); cikti.push( ` "${ulke.kod}": {`, ` ad: ${JSON.stringify(ulke.ad)},`, ` proje: { ax: ${yaz(s * kx)}, bx: ${yaz(ox - s * minX)}, ay: ${yaz(-s)}, by: ${yaz(oy - s * minY)} },`, ` ulke: ${JSON.stringify(vurguluPaths)},`, ` baglam: ${JSON.stringify(baglamPaths)},`, " },", ); console.log( `${ulke.kod}: ${vurguluPaths.length} vurgulu + ${baglamPaths.length} bağlam path, ` + `~${Math.round((vurguluPaths.join("").length + baglamPaths.join("").length) / 1024)}KB`, ); } cikti.push("};", ""); const hedef = path.join( process.cwd(), "src", "lib", "harita-ulkeler-verisi.ts", ); fs.writeFileSync(hedef, cikti.join("\n")); console.log("yazıldı:", hedef); } main();