DEV Community

Lucas Lim
Lucas Lim

Posted on

Trouble with laying two SVG Treemaps on one page. (with D3.js)

Hello,
I am new to D3.js and I had trouble laying two SVG Treemaps on one page.
I got a hint from this.

What I want to do is lay two zoomable treemap elements on one page. (The sample above is for one treemap.)
I implemented the code myself, but not working properly.

Image description

Please help me if you are familiar with SVG and have experience with the zoomable D3 treemap.

chart1.js

fetch('../data/chart1.json')
  .then((response) => response.json())
  .then((json) => {
    render(json, 'investicija');
    render(json, 'proizvod');
  })
  .catch((error) => console.error('Error fetching data:', error));

function render(treemapData, selector) {
  const renderData = treemapData[selector][0]; // Ensure this key exists in your JSON data

  (function () {
    const margin = { top: 20, right: 0, bottom: 0, left: 0 };
    const container = document.getElementById(selector);
    const width = container.clientWidth;
    const height = container.clientHeight;

    let svg, grandparent, scaleX, scaleY, treemap;

    function createSvg() {
      scaleX = d3.scale.linear()
        .domain([0, width])
        .range([0, width]);

      scaleY = d3.scale.linear()
        .domain([0, height])
        .range([0, height]);

      treemap = d3.layout.treemap()
        .children((d, depth) => depth ? null : d._children)
        .sort((a, b) => a.amount - b.amount)
        .ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
        .round(false)
        .value(d => d.value);

      svg = d3.select(`#${selector}`).append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.bottom + margin.top)
        .style("margin-left", -margin.left + "px")
        .style("margin-right", -margin.right + "px")
        .append("g")
        .attr("transform", `translate(${margin.left},${margin.top})`)
        .style("shape-rendering", "geometricPrecision");

      grandparent = svg.append("g")
        .attr("class", "grandparent");

      grandparent.append("rect")
        .attr("y", -margin.top)
        .attr("width", width)
        .attr("height", margin.top + 10);

      grandparent.append("text")
        .attr("x", 25)
        .attr("y", 6 - margin.top + 3)
        .attr("dy", ".75em");
    }

    function initialize(d) {
      d.x = d.y = 0;
      d.dx = width;
      d.dy = height;
      d.depth = 0;
    }

    function accumulate(d) {
      return (d._children = d.children) ? d.amount = d.children.reduce((p, v) => p + accumulate(v), 0) : d.amount;
    }

    function layout(d) {
      if (d._children) {
        treemap.nodes({ _children: d._children });

        d._children.forEach(c => {
          c.x = d.x + c.x * d.dx;
          c.y = d.y + c.y * d.dy;
          c.dx *= d.dx;
          c.dy *= d.dy;
          c.parent = d;
        });
      }
    }

    function display(d) {
      grandparent
        .datum(d.parent)
        .on("click", transition)
        .select("text")
        .text(selector);

      const g1 = svg.insert("g", ".grandparent")
        .datum(d)
        .attr("class", "depth");

      const g = g1.selectAll("g")
        .data(d._children)
        .enter().append("g");

      g.filter(d => d._children)
        .classed("children", true)
        .on("click", transition);

      g.append("rect")
        .attr("class", "parent")
        .call(rect)
        .style("fill", d => d.color)
        .append("title")
        .text(d => d.fullname);

      g.selectAll(".child")
        .data(d => {
          layout(d);
          return d._children || [d];
        })
        .enter().append("rect")
        .attr("class", "child")
        .call(rect);

      g.append("text")
        .classed("overlaidText1", true)
        .text(d => d.name)
        .call(middletext1);

      g.append("text")
        .classed("overlaidText", true)
        .text(d => d.text)
        .call(middletext);

      function transition(d) {
        if (!d || this.transitioning) return;
        this.transitioning = true;

        const g2 = display(d),
          t1 = g1.transition().duration(1000),
          t2 = g2.transition().duration(1000);

        scaleX.domain([d.x, d.x + d.dx]);
        scaleY.domain([d.y, d.y + d.dy]);

        svg.style("shape-rendering", null);

        svg.selectAll(".depth").sort((a, b) => a.depth - b.depth);

        g2.selectAll("text")
          .style("fill-opacity", 0);

        t1.selectAll("text:not(.overlaidText)").call(middletext1).style("fill-opacity", 0);
        t2.selectAll("text:not(.overlaidText)").call(middletext1).style("fill-opacity", 1);
        t1.selectAll(".overlaidText").call(middletext).style("fill-opacity", 0);
        t2.selectAll(".overlaidText").call(middletext).style("fill-opacity", 1);
        t1.selectAll("rect").call(rect);
        t2.selectAll("rect").call(rect);

        t1.remove().each("end", () => {
          svg.style("shape-rendering", "geometricPrecision");
          this.transitioning = false;
        });
      }

      return g;
    }

    function text(text) {
      text.attr("x", d => scaleX(d.x) + 6)
        .attr("y", d => scaleY(d.y) + 2);
    }

    function middletext(text) {
      text.attr("x", d => scaleX(d.x + d.dx / 2))
        .attr("y", d => scaleY(d.y + d.dy / 2) + 18);
    }

    function middletext1(text) {
      text.attr("x", d => scaleX(d.x + d.dx / 2))
        .attr("y", d => scaleY(d.y + d.dy / 2) - 3.5);
    }

    function rect(rect) {
      rect.attr("x", d => scaleX(d.x))
        .attr("y", d => scaleY(d.y) - 6)
        .attr("width", d => {
          const x0 = scaleX(d.x);
          const x1 = scaleX(d.x + d.dx);
          const w = x1 - x0;
          console.log(`Width for ${d.name} in ${selector}: x0=${x0}, x1=${x1}, width=${w}`);
          return w;
        })
        .attr("height", d => scaleY(d.y + d.dy) - scaleY(d.y))
        .attr("rx", "0px");
    }


    // Debug logs
    createSvg();
    initialize(renderData);
    accumulate(renderData);
    layout(renderData);
    display(renderData);
  })();
}

Enter fullscreen mode Exit fullscreen mode

chart1.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>D3-Stacked-Bar-Chart (Percentage)</title>
    <link href="css/chart1.css" rel="stylesheet" type="text/css" />
</head>

<body>
    <div>
        <div id="investicija" style="display: inline-block; width: 500px; height: 300px;"  ></div>
        <div id="proizvod" style="display: inline-block; width: 500px; height: 300px;"></div>
    </div>
</body>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="script/chart1.js" charset="utf-8"></script>

</html>
Enter fullscreen mode Exit fullscreen mode

chart1.json

{
    "investicija": [
        {
            "value": 3500000,
            "kvadrata": 1880,
            "invest_kvadrat": 1862,
            "description": "<p class='desc'>Opis investicije:<br>Stambena zgrada...</p>",
            "children": [
                {
                    "name": "PLAC",
                    "value": 1300000,
                    "image": "some_image.jpg",
                    "link": "http://somelink.com?id=1",
                    "color": "#6060e0"
                },
                {
                    "name": "SOFTCOST",
                    "value": 300000,
                    "color": "#ff8259",
                    "children": [
                        {
                            "name": "Nadzor",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?id=1",
                            "value": 100000
                        },
                        {
                            "name": "Dozvola",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?id=1",
                            "value": 100000
                        },
                        {
                            "name": "Direkcija",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?id=1",
                            "value": 100000
                        }
                    ]
                },
                {
                    "name": "HARDCOST",
                    "value": 1900000,
                    "color": "#fff214",
                    "image": "some_image.jpg",
                    "link": "http://somelink.com?id=1",
                    "children": [
                        {
                            "name": "Izvodjenje",
                            "value": 600000,
                            "children": [
                                {
                                    "job_id": "1",
                                    "name": "Obezbedjenje temeljne jame",
                                    "value": 200000,
                                    "izvodjac": "InKop",
                                    "izvodjac_id": 21,
                                    "trajanje": 33,
                                    "image": "some_image.jpg",
                                    "link": "http://somelink.com?job_id=1",
                                    "rok": "20-12-2024"
                                },
                                {
                                    "job_id": "2",
                                    "name": "Iskop",
                                    "value": 200000,
                                    "izvodjac": "InKop",
                                    "izvodjac_id": 21,
                                    "trajanje": 33,
                                    "image": "some_image.jpg",
                                    "link": "http://somelink.com?job_id=1",
                                    "rok": "20-12-2024"
                                },
                                {
                                    "job_id": "3",
                                    "name": "Temeljna ploca",
                                    "value": 200000,
                                    "izvodjac": "InKop",
                                    "izvodjac_id": 21,
                                    "trajanje": 33,
                                    "image": "some_image.jpg",
                                    "link": "http://somelink.com?job_id=1",
                                    "rok": "20-12-2024"
                                }
                            ]
                        },
                        {
                            "name": "Materijal",
                            "value": 1300000,
                            "children": [
                                {
                                    "order_id": "3",
                                    "name": "Armaturna mreza",
                                    "value": 300000,
                                    "dobavljac": "Metal metal",
                                    "dobavljac_id": 22,
                                    "image": "some_image.jpg",
                                    "link": "http://somelink.com?job_id=1",
                                    "isporuka": "20-12-2024"
                                },
                                {
                                    "order_id": "3",
                                    "name": "Plocice",
                                    "value": 300000,
                                    "dobavljac": "RA KERAMIKA",
                                    "dobavljac_id": 21,
                                    "image": "some_image.jpg",
                                    "link": "http://somelink.com?job_id=1",
                                    "isporuka": "20-12-2024"
                                },
                                {
                                    "order_id": "3",
                                    "name": "Lepak za fasadu",
                                    "value": 700000,
                                    "dobavljac": "ROMA",
                                    "dobavljac_id": 25,
                                    "image": "some_image.jpg",
                                    "link": "http://somelink.com?job_id=1",
                                    "isporuka": "20-12-2024"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ],
    "proizvod": [
        {
            "value": 5100000,
            "kvadrata": 1880,
            "invest_kvadrat": 2712,
            "children": [
                {
                    "name": "Stanovi",
                    "kvadrata": 1284,
                    "komada": 21,
                    "color": "#6060e0",
                    "children": [
                        {
                            "name": "Stan 1",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "sprat": 1,
                            "kvadrata": 45.44,
                            "value": 1200000
                        },
                        {
                            "name": "Stan 2",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "sprat": 2,
                            "kvadrata": 85.5,
                            "value": 2300000
                        },
                        {
                            "name": "Stan 3",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "sprat": 2,
                            "kvadrata": 152,
                            "value": 3300000
                        }
                    ]
                },
                {
                    "name": "Lokali",
                    "kvadrata": 480,
                    "komada": 2,
                    "color": "#ff8259",
                    "children": [
                        {
                            "name": "Lokal 1",
                            "opis": "Lep s pogledom na ulicu",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "kvadrata": 152,
                            "value": 4300000
                        },
                        {
                            "name": "Lokal 2",
                            "opis": "Na 2 sprata, idealan za restoran",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "kvadrata": 152,
                            "value": 1300000
                        },
                        {
                            "name": "Lokal 3",
                            "opis": "Veliki magacin",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "kvadrata": 152,
                            "value": 1300000
                        }
                    ]
                },
                {
                    "name": "Garažna mesta",
                    "kvadrata": 980,
                    "komada": 21,
                    "color": "#fff214",
                    "children": [
                        {
                            "name": "Garažno mesto 1",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "nivo": "-2",
                            "kvadrata": 12.5,
                            "value": 20000
                        },
                        {
                            "name": "Garažno mesto 2",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "nivo": "-2",
                            "kvadrata": 12.5,
                            "value": 20000
                        },
                        {
                            "name": "Garažno mesto 3",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "nivo": "-1",
                            "kvadrata": 12.5,
                            "value": 20000
                        },
                        {
                            "name": "Garažno mesto 4",
                            "image": "some_image.jpg",
                            "link": "http://somelink.com?job_id=1",
                            "nivo": "-1",
                            "kvadrata": 12.5,
                            "value": 20000
                        }
                    ]
                }
            ]
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️