// Şehir haritası (DESIGN.md §4.17) için il ve ülke kontürlerini hero'nun // dot-matrix ızgarasına rasterize eder. Hero'daki TurkeyPixelMap 64 sütun; // burada 56 sütun (Bilal, 24 Eyl 2026: "piksel detaylarını azalt"); her il en az birkaç kare alsın (Yalova, Kilis gibi // küçük iller için 2×2 blok garantisi vardır). Çıktı // src/lib/il-izgara-verisi.ts dosyasına yazılır ve commit'lenir. // // Kaynak: turkey-map-react il path'leri (viewBox 1050×585, üstten 138 kırpık) // ve harita-ulkeler-verisi.ts ülke kontürleri (1050×447). Hücre merkezi // poligonun içindeyse hücre o ile aittir (ray casting; Bézier eğrileri uç // noktalarıyla düzleştirilir — hücre ölçeğinde fark etmez). // // Çalıştırma: pnpm tsx scripts/il-izgara-uret.ts import fs from "node:fs"; import path from "node:path"; import { cities } from "turkey-map-react/lib/data"; import { normalizeIlAdi, HARITA_UST_KIRPMA, HARITA_GENISLIK, HARITA_YUKSEKLIK } from "../src/lib/harita"; import { ULKE_HARITALARI } from "../src/lib/harita-ulkeler-verisi"; const SUTUN = 56; const HUCRE = HARITA_GENISLIK / SUTUN; const YUKSEKLIK = HARITA_YUKSEKLIK - HARITA_UST_KIRPMA; // 447 const SATIR = Math.ceil(YUKSEKLIK / HUCRE); type Nokta = [number, number]; type Hucre = [number, number]; function poligonlar(d: string): Nokta[][] { const toks = d.match(/[MLHVCZmlhvcz]|-?\d*\.?\d+(?:e-?\d+)?/g) ?? []; const polys: Nokta[][] = []; let cur: Nokta[] = []; let x = 0; let y = 0; let cmd = ""; let i = 0; const num = () => Number(toks[i++]); const kapat = () => { if (cur.length > 2) polys.push(cur); cur = []; }; while (i < toks.length) { const t = toks[i]; if (/[A-Za-z]/.test(t)) { cmd = t; i++; if (cmd === "Z" || cmd === "z") kapat(); continue; } switch (cmd) { case "M": x = num(); y = num(); kapat(); cur = [[x, y]]; cmd = "L"; break; case "m": x += num(); y += num(); kapat(); cur = [[x, y]]; cmd = "l"; break; case "L": x = num(); y = num(); cur.push([x, y]); break; case "l": x += num(); y += num(); cur.push([x, y]); break; case "H": x = num(); cur.push([x, y]); break; case "h": x += num(); cur.push([x, y]); break; case "V": y = num(); cur.push([x, y]); break; case "v": y += num(); cur.push([x, y]); break; case "C": num(); num(); num(); num(); x = num(); y = num(); cur.push([x, y]); break; case "c": num(); num(); num(); num(); x += num(); y += num(); cur.push([x, y]); break; default: i++; } } kapat(); return polys; } function icinde(px: number, py: number, poly: Nokta[]): boolean { let ic = false; for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) { const [xi, yi] = poly[i]; const [xj, yj] = poly[j]; if (yi > py !== yj > py && px < ((xj - xi) * (py - yi)) / (yj - yi) + xi) ic = !ic; } return ic; } /** Her hücre merkezini ilk eşleşen bölgeye verir; sıra öncelik demektir. */ function rasterize( bolgeler: { ad: string; polys: Nokta[][] }[], ustKirpma: number, ): Map { const sonuc = new Map(); for (let r = 0; r < SATIR; r++) { for (let q = 0; q < SUTUN; q++) { const px = (q + 0.5) * HUCRE; const py = ustKirpma + (r + 0.5) * HUCRE; const b = bolgeler.find((b) => b.polys.some((p) => icinde(px, py, p))); if (!b) continue; const liste = sonuc.get(b.ad) ?? []; liste.push([q, r]); sonuc.set(b.ad, liste); } } return sonuc; } // ---- Türkiye illeri const iller = cities.map((c) => ({ ad: normalizeIlAdi(c.name), polys: poligonlar(c.path) })); const ilHucre = rasterize(iller, HARITA_UST_KIRPMA); // Küçük il garantisi: 4 hücreden azı olan il bbox merkezinde 2×2 blok alır, // o hücreler komşulardan düşer. for (const il of iller) { const mevcut = ilHucre.get(il.ad) ?? []; if (mevcut.length >= 4) continue; const pts = il.polys.flat(); const cx = (Math.min(...pts.map((p) => p[0])) + Math.max(...pts.map((p) => p[0]))) / 2; const cy = (Math.min(...pts.map((p) => p[1])) + Math.max(...pts.map((p) => p[1]))) / 2; const q0 = Math.min(SUTUN - 2, Math.max(0, Math.round(cx / HUCRE) - 1)); const r0 = Math.min(SATIR - 2, Math.max(0, Math.round((cy - HARITA_UST_KIRPMA) / HUCRE) - 1)); const blok: Hucre[] = [[q0, r0], [q0 + 1, r0], [q0, r0 + 1], [q0 + 1, r0 + 1]]; const anahtar = new Set(blok.map(([q, r]) => `${q},${r}`)); for (const [ad, liste] of ilHucre) { if (ad === il.ad) continue; ilHucre.set(ad, liste.filter(([q, r]) => !anahtar.has(`${q},${r}`))); } const birlesik = new Map(mevcut.map((h) => [`${h[0]},${h[1]}`, h])); for (const h of blok) birlesik.set(`${h[0]},${h[1]}`, h); ilHucre.set(il.ad, [...birlesik.values()]); } // ---- Ülkeler (KKTC, yurt dışı): ülke öncelikli, bağlam ikincil const ulkeHucre: Record = {}; for (const [kod, harita] of Object.entries(ULKE_HARITALARI)) { const r = rasterize( [ { ad: "ulke", polys: harita.ulke.flatMap(poligonlar) }, { ad: "baglam", polys: harita.baglam.flatMap(poligonlar) }, ], 0, ); ulkeHucre[kod] = { ulke: r.get("ulke") ?? [], baglam: r.get("baglam") ?? [] }; } const enKucuk = [...ilHucre.entries()].sort((a, b) => a[1].length - b[1].length).slice(0, 5); console.log( `${SUTUN}×${SATIR}, hücre ${HUCRE.toFixed(3)}; kara ${[...ilHucre.values()].reduce((a, b) => a + b.length, 0)}; en küçük: ${enKucuk.map(([a, l]) => `${a}:${l.length}`).join(" ")}`, ); const satirlar = [...ilHucre.entries()] .sort((a, b) => a[0].localeCompare(b[0], "tr-TR")) .map(([ad, liste]) => ` ${JSON.stringify(ad)}: ${JSON.stringify(liste)},`); const ulkeSatirlar = Object.entries(ulkeHucre).map( ([kod, v]) => ` ${JSON.stringify(kod)}: { ulke: ${JSON.stringify(v.ulke)}, baglam: ${JSON.stringify(v.baglam)} },`, ); const cikti = `// Bu dosya scripts/il-izgara-uret.ts tarafından üretildi — elle düzenleme. // Hero'nun dot-matrix ızgarasında (${SUTUN} sütun × ${SATIR} satır, hücre ${HUCRE.toFixed(4)} // viewBox birimi) her ilin ve ülkenin kapladığı hücreler [sütun, satır]. import type { UlkeKodu } from "./harita-ulkeler-verisi"; export const IL_IZGARA = { sutun: ${SUTUN}, satir: ${SATIR}, hucre: ${HUCRE.toFixed(4)}, } as const; export type IzgaraHucre = readonly [number, number]; /** Anahtar DB biçiminde il adı (normalizeIlAdi). */ export const IL_HUCRELERI: Record = { ${satirlar.join("\n")} }; export const ULKE_HUCRELERI: Record = { ${ulkeSatirlar.join("\n")} }; `; const hedef = path.join(process.cwd(), "src/lib/il-izgara-verisi.ts"); fs.writeFileSync(hedef, cikti); console.log("yazıldı:", hedef, `${(cikti.length / 1024).toFixed(0)} KB`);