• INICIO
  • PROYECTOS
  • GLOSARIO
  • BLOG
links = d3.csvParse(await FileAttachment("categories.csv").text())
types = Array.from(new Set(links.map(d => d.type)))
data = ({ nodes: Array.from(new Set(links.flatMap(l => [l.source, l.target])), id => ({ id })), links })
height = 600
color = d3.scaleOrdinal(types, d3.schemeCategory10)

function linkArc(d) {
    const r = Math.hypot(d.target.x - d.source.x, d.target.y - d.source.y);
    return `
    M${d.source.x},${d.source.y}
    A${r},${r} 0 0,1 ${d.target.x},${d.target.y}
    `;
}

drag = simulation => {

    function dragstarted(event, d) {
        if (!event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(event, d) {
        d.fx = event.x;
        d.fy = event.y;
    }

    function dragended(event, d) {
        if (!event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
    }

    return d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended);
}


import {Swatches} from "@d3/color-legend"


chart = {
    const links = data.links.map(d => Object.create(d));
    const nodes = data.nodes.map(d => Object.create(d));

    // Calculate min and max id length
    const minLength = d3.min(nodes, d => d.id.length);
    const maxLength = d3.max(nodes, d => d.id.length);

    nodes.forEach(node => {
        node.sizeValue = links.filter(link => link.source === node.id || link.target === node.id).length;
        // Define idLength with minmax scale to get a value between 0 and 1
        node.idLength = d3.scaleLinear()
            .domain([minLength, maxLength])
            .range([0, 1])(node.id.length);
        // Replace the zero value with a small value to avoid a division by zero
        node.idLength = node.idLength === 1 ? node.idLength : node.idLength + 0.1;
    });

    // Calculate min and max sizeValue
    const minValue = d3.min(nodes, d => d.sizeValue);
    const maxValue = d3.max(nodes, d => d.sizeValue);

    // Define size scale
    const sizeScale = d3.scaleLinear()
        .domain([minValue, maxValue])
        .range([0.7, 1]);


    const simulation = d3.forceSimulation(nodes)
        .force("link", d3.forceLink(links).id(d => d.id))
        .force("charge", d3.forceManyBody().strength(-400))
        .force("x", d3.forceX().strength(0.5).x(-width / 5))
        .force("y", d3.forceY().strength(0.5).y(0));

    const svg = d3.create("svg")
        .attr("viewBox", [-width / 2, -height*.65 / 2, width, height*.65])
        .style("font", "12px sans-serif")
        .style("border", "1px solid black")
        // background color degrade
        .style("background", "linear-gradient(45deg, #4DB6AC 0%, #002538 100%)")
        // Add an id to the svg to be able to select it
        .attr("id", "network");

    // Per-type markers, as they don't inherit styles.
    svg.append("defs").selectAll("marker")
        .data(types)
        .join("marker")
        .attr("id", d => `arrow-${d}`)
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 15)
        .attr("refY", -0.5)
        .attr("markerWidth", 6)
        .attr("markerHeight", 6)
        .attr("orient", "auto")
        .append("path")
        .attr("fill", color)
        .attr("d", "M0,-0L10,0L0,0");
        //.attr("d", "M0,-5L10,0L0,5");

    const link = svg.append("g")
        .attr("fill", "none")
        .attr("stroke-width", 1.5)
        .selectAll("path")
        .data(links)
        .join("path")
        // .attr("stroke", d => color(d.type))
        .attr("stroke", d => 'orange')
        .attr("marker-end", d => `url(${new URL(`#arrow-${d.type}`, location)})`);

    const node = svg.append("g")
        .attr("fill", "currentColor")
        .attr("stroke-linecap", "round")
        .attr("stroke-linejoin", "round")
        .selectAll("g")
        .data(nodes)
        .join("g")
        .call(drag(simulation));

    // node.append("circle")
    //     .attr("stroke", "white")
    //     .attr("stroke-width", 1.5)
    //     .attr("r", 4);

    //node.append("text")
    //        .attr("x", 8)
    //        .attr("y", "0.31em")
    //        .html(d => `<a class="category2" href="./blog/#category=${d.id}">${d.id + d.sizeValue}</a>`) // aquí
    //        .style("font-size", d => `${sizeScale(d.sizeValue)}rem`)
    //        .clone(true).lower()
    //        .attr("fill", "none")
    //        .attr("stroke", "white")
    //        .attr("stroke-width", 3);
//
    //node.append("rect")
    //        .attr("x", 8)
    //        .attr("y", "0.31em")
    //        .attr("width", d => `${sizeScale(d.sizeValue) * 8}rem`)
    //        .attr("height", d => `${sizeScale(d.sizeValue)}rem`)
    //        .style("fill", "white")
    //        .style("stroke", "black")
    //        .style("stroke-width", "1px")

node.append("text")
    .attr("x", 8)
    .attr("y", "0.31em")
    .html(d => `<a class="category2" href="./blog/#category=${d.id}">${d.id}</a>`)
    .style("font-size", d => `${sizeScale(d.sizeValue)}rem`)
    .clone(true).lower()
    .attr("fill", "none")


node.insert("rect", "text") // insert rect before text
    .attr("x", 5)
    .attr("y", "-1em")
    .attr("width", d => `${(d.idLength * 11)*sizeScale(d.sizeValue)}` + "rem")
    .attr("height", "2.1em")
    .attr("fill", "white")
    // add rounded corners
    .attr("rx", "0.5em")



    simulation.on("tick", () => {
        link.attr("d", linkArc);
        node.attr("transform", d => `translate(${d.x},${d.y})`);
    });

    invalidation.then(() => simulation.stop());

    return svg.node();
}
  • Copyright © 2022 Adatar. All rights reserved.
Cookie Preferences