• Mahkeme
    • Özet
    • İfadeler
    • Savunma
    • Gerekçeli Karar
  • Yazılar
  • Yardım
    • Kılavuz
    • Eksik Bildir
    • Soru sor
    • SSS
function turkishLower(str) {
  // Turkish-aware lowercase conversion: I → ı, İ → i
  return str
    .replace(/I/g, "ı")
    .replace(/İ/g, "i")
    .toLowerCase();
}

// ─────────────────────────────
// 2. Load data (frequencies + labels)
// ─────────────────────────────

// Word frequencies JSON (same folder as the qmd)
data = await FileAttachment("judgment_words.json").json()

// Label definitions JSON (from site root)
labels = await fetch("/resources/json/word_labels.json").then(r => r.json())

// Convert { label: [words...] } into Map(word -> label)
labelByWord = new Map(
  Object.entries(labels).flatMap(([label, words]) =>
    words.map(w => [w, label])
  )
)

// Lower-case copy of data for matching logic
dataLower = data.map(d => ({
  text: turkishLower(d.text),   // used only for pattern matching
  value: d.value
}))

// ─────────────────────────────
// 3. Scales and label domain
// ─────────────────────────────

// Frequency extent
valueExtent = d3.extent(data, d => d.value)

// Opacity scale → visibility control
opacityScale = d3.scaleLinear()
  .domain(valueExtent)
  .range([0.4, 1])   // low frequency: faint, high frequency: fully opaque

// Derive labelDomain directly from category keys and append "other"
labelDomain = [...Object.keys(labels), "other"]

// Label → base color (category-based, not frequency-based)
labelColor = d3.scaleOrdinal()
  .domain(labelDomain)
  .range(d3.schemeSet2)

// Precompute label patterns in lowercase, sorted by pattern length
// so that more specific patterns are matched first.
labelPatterns = Array.from(labelByWord.entries())
  .map(([pattern, label]) => ({
    patternLower: turkishLower(pattern),
    label,
    length: pattern.length
  }))
  .sort((a, b) => d3.descending(a.length, b.length))

// Decide which label to use for a given word:
//  - lowerText is the Turkish-lowered version of the word
//  - if any patternLower is contained in lowerText, use that label
//  - fall back to "other" if no pattern matches
getLabel = (origText, lowerText) => {
  for (const { patternLower, label } of labelPatterns) {
    if (lowerText.includes(patternLower)) {
      return label
    }
  }
  return "other"
}

// Final color function for each word (category-based only)
// Frequency is handled via opacity, not lightness.
colorFor = (d, i) => {
  const lowerText = dataLower[i].text
  const label = getLabel(d.text, lowerText)

  // For "other", use the CSS variable directly so it updates with the theme.
  if (label === "other") {
    return "var(--bs-body-color)"
  }

  // For labeled categories, use the categorical palette (static colors).
  return labelColor(label)
}

// Opacity function driven by frequency
opacityFor = value => opacityScale(value)

// ─────────────────────────────
// 4. Layout configuration (d3-cloud)
// ─────────────────────────────

// Load the d3-cloud module
cloud = require("d3-cloud@1")

// Size limits for the cloud
MIN_WIDTH = 320
MAX_WIDTH = 900
MIN_HEIGHT = 500
MAX_HEIGHT = 600

// Actual width to use (clamp the container width into [MIN_WIDTH, MAX_WIDTH])
effectiveWidth = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, width))

// As width decreases, height increases (linear inverse relationship):
// width = MIN_WIDTH  → height ≈ MAX_HEIGHT
// width = MAX_WIDTH  → height ≈ MIN_HEIGHT
effectiveHeight = {
  const t = (effectiveWidth - MIN_WIDTH) / (MAX_WIDTH - MIN_WIDTH)  // [0,1]
  return MAX_HEIGHT - t * (MAX_HEIGHT - MIN_HEIGHT)
}

// Layout: width and height are reactive; this cell will rerun
// when the page is resized.
layout = {
  const maxValue = d3.max(data, d => d.value)

  // More aggressive font scaling (make high-frequency words much larger)
  const sizeScale = d3.scalePow().exponent(1.6)
    .domain([1, maxValue])
    .range([10, 50])

  // Inner drawing area (slightly smaller than the effective size)
  const innerWidth = effectiveWidth * 0.9
  const innerHeight = effectiveHeight * 0.9

  const c = cloud()
    .size([innerWidth, innerHeight])
    .words(
      data.map(d => ({
        text: d.text,
        size: sizeScale(d.value),
        value: d.value
      }))
    )
    .padding(2)
    .rotate(() => 0)
    .font("sans-serif")
    .fontSize(d => d.size)
    .spiral("archimedean")

  // The promise resolves with the positioned words and inner dimensions
  return new Promise(resolve => {
    c.on("end", words => {
      resolve({ words, innerWidth, innerHeight })
    }).start()
  })
}

// ─────────────────────────────
// 5. Render SVG word cloud
// ─────────────────────────────

chart = {
  const { words, innerWidth, innerHeight } = await layout

  const svg = d3.create("svg")
    .attr("viewBox", [
      -innerWidth / 2,
      -innerHeight / 2,
      innerWidth,
      innerHeight
    ])
    .attr("width", innerWidth)
    .attr("height", innerHeight)
    // Allow it to scale with the container width while keeping aspect ratio
    .attr("style", "max-width: 100%; height: auto; display: block; margin: 0 auto;")

  svg.append("g")
    .selectAll("text")
    .data(words)
    .join("text")
      .attr("text-anchor", "middle")
      .attr("font-family", "sans-serif")
      .attr("font-size", d => d.size)
      .attr("fill", (d, i) => colorFor(d, i))
      .attr("fill-opacity", d => opacityFor(d.value))
      .attr("transform", d => `translate(${d.x},${d.y})rotate(${d.rotate})`)
      .text(d => d.text)
      .call(text => text.append("title")
                        .text(d => `${d.text} — ${d.value} kez`))

  return svg.node()
}
Yukarıya çık
  • Güncelleme29 Kas 2025 17:53 UTC
  • Derleme süresi⏱️ 0 ms
  • Hakkında
  • SSS
  • Bu sayfayı düzenle
  • Bir sorun bildir