Append two elements in svg at the same level

Viewed 443

I'm using d3.js

Hi, I'm having an issue finding how to append two elements (path and image) to the same g (inside my svg) from the same data. I know how to do this, but the tricky thing is I need to get the BBox values of the "path" elements in order to place the "image" elements in the middle... My goal is actually to place little clouds in the center of cities on a map like this : this is the map I am trying to reproduce

On the map it's not centered but I have to do so. So this is my current code:

// Draw the map
svg.append("g")
    .selectAll("path")
    .data(mapEPCI.features)
    .enter()
    .append("path")
    .attr("fill", d => d.properties.color)
    .attr("d", d3.geoPath().projection(projection))
    .style("stroke", "white")
    .append("image")
        .attr("xlink:href", function(d) {
            if (d.properties.plan_air == 1)
                return ("data/page8_territoires/cloud.png")
            else if (d.properties.plan_air == 2)
                return ("data/page8_territoires/cloudgray.png")
        })
        .attr("width", "20")
        .attr("height", "15")
        .attr("x", function (d) {
            let bbox = d3.select(this.parentNode).node().getBBox();
            return bbox.x + 30})
        .attr("y", function (d) {
            return d3.select(this.parentNode).node().getBBox().y + 30})

This gets the right coordinates for my images but it's because the parent node is actually the path... If I append the image to the g element, is there a way to get the "BrotherNode", or maybe the last child of the "g" element ? I don't know if I'm clear enough but I hope you get my point.

I'm kinda new to js so maybe I'm missing something simple I just don't know yet

Thanks for your help

1 Answers

I would handle your data at the g level and create a group for every map feature (country) which contains the path and a sibling image:

<!doctype html>

<html>

<head>
  <script src="https://d3js.org/d3.v6.min.js"></script>
</head>

<body>
  <svg width="600" height="600"></svg>
  <script>
    let svg = d3.select('svg'),
      mapEPCI = {
        features: [100, 200, 300, 400]
      };

    let g = svg.selectAll('g')
      .data(mapEPCI.features)

    // enter selection is collection of g
    let ge = g.enter().append("g");

    // append a path to each g according to data
    ge.append('path')
      .attr("d", (d) => "M" + d + ",10L" + d + ",100")
      .style("stroke", "black");

    // append a sibling image
    ge.append("image")
      .attr("xlink:href", "https://placeimg.com/20/15/animals")
      .attr("width", "20")
      .attr("height", "15")
      .attr("transform", function(d) {
        // find my sibling path to get bbox
        let sibling = this.parentNode.firstChild;
        let bbox = sibling.getBBox();
        return "translate(" + (bbox.x - 20 / 2) + "," + (bbox.y + bbox.height / 2 - 15 / 2) + ")"
      });
  </script>
</body>

</html>

Related