Home > front end >  Add text element to the center of path element
Add text element to the center of path element

Time:11-14

I'm creating an Choropleth Map. There I need to make group each svg path & title for hover effect. Now I got stuck to positioning of text of it's path

Please check the code snippet. How do I add a each Text at it's Path center position? Currently the text has been placed at the top.

Note: I just use path, not coordinates!
Note2: Mute/Unmute console.log for view map.

document.addEventListener("DOMContentLoaded", function () {
  const h = 400;
  const w = 400;

  var svg = d3
    .select(".map")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .attr("viewBox", "0 0 400 400")
    
  var groups = svg
    .selectAll(".groups")
    .data(map)
    .enter()
    .append("g")
    .attr("class", "region");

  groups
    .append("path")
    .attr("d", (m) => m.path)
    .attr("stroke-width", "1")
    .attr("stroke", "#fff")
    .attr("fill","#333")

  groups
    .append("text")
    .text((m) => m.title)
    .style("font-weight", "bold")
    .style("font-family", "Jersey")
    .attr("fill","#6D6E71")
    .attr("text-anchor", "middle")
    .style("text-transform", "uppercase")
    .attr("x", (m, i, nodes) => {
      let bbox = d3.select(nodes[i]).node().getBBox();
      let centreX = bbox.width   bbox.x;
       //console.log(d3.select(nodes[i]).node().getBBox());
      // console.log("X", centreX);
      return centreX;
    })
    .attr("y", (m, i, nodes) => {
      let bbox = d3.select(nodes[i]).node().getBBox();
      let centreY = bbox.height   bbox.y;
      // console.log(d3.select(nodes[i]).node().getBBox());
      // console.log("Y", centreY);
      return centreY;
    });
});

const map=[{
    id: 0,
    title: "A",
    path: "M 45.25 34.96 l -0.97 -3.35 l -0.97 -2.39 l 2.43 -3.35 l 1.94 -4.31 l 1.94 -5.74 l 3.89 -5.74 l 0.97 6.22 l 0.49 3.35 l 3.89 3.83 l 3.89 -0.96 l 0.97 3.35 l 3.89 0.96 l 0.49 2.87 l 3.89 1.44 l 0.49 1.91 L 76.36 34 l 2.43 -2.87 l 1.46 3.83 l 1.94 2.39 l 2.92 1.91 l 0.97 1.91 l 2.43 -0.96 l 1.46 1.44 l -1.46 3.35 l 1.46 1.44 l 1.94 1.44 v 0.96 l 3.89 -0.48 v 1.91 l 0.97 1.91 v 2.39 l 2.43 1.91 l -0.49 4.31 l 1.94 3.35 l 2.43 3.35 l -0.97 3.35 v 4.79 v 4.31 l -1.46 3.83 l 0.97 4.79 l -2.43 3.35 l -3.4 4.79 l -3.89 -0.96 l -5.35 -0.48 l -1.94 -3.83 l -2.43 -1.91 l 2.92 -5.26 l -1.94 -3.83 l -2.43 -2.87 l -3.89 -1.44 l -1.94 -1.91 l -1.46 -2.87 l -2.43 0.96 c 0 0 -0.61 -0.48 -1.94 -0.96 s -3.4 -0.96 -3.4 -0.96 l -2.43 -0.96 l -2.43 1.91 l -3.89 -0.96 l -3.4 -1.44 l -2.92 1.91 l -0.49 2.39 c 0 0 -1.58 -0.24 -3.4 -0.96 c -1.82 -0.72 -3.89 -1.91 -3.89 -1.91 l -0.97 -1.44 l 3.4 -1.91 l 1.94 -2.39 l -1.46 -2.87 v -6.7 l 5.35 -0.48 l 1.94 -1.91 l 1.94 -2.87 l 4.37 -1.44 l 0.97 -3.35 l 2.43 3.83 l 2.43 -1.44 l 1.94 -3.35 l -1.94 -2.39 l -1.94 -2.87 v -3.35 l -3.4 -2.87 l -4.37 0.96 l -2.43 -3.83 l -2.43 1.91 l -2.92 -0.48 L 45.25 34.96 Z",
  },
  {
    id: 1,
    title: "B",
    path: "M 19.49 126.38 l -2.43 -4.31 l -2.92 -3.83 l 1.94 -4.31 l -4.86 -0.96 l 1.46 -7.66 l 3.4 -2.39 l -0.49 -2.87 l 1.94 -3.83 l 5.83 -3.83 l 1.94 -5.26 l -4.37 -2.39 l 2.43 -4.31 l 0.97 -4.31 l 5.35 -1.91 v -4.79 l 7.29 0.96 l 5.35 -1.44 l 7.29 4.79 l 1.46 -3.83 l 4.96 -1.03 l 4.27 1.51 l 3.89 -0.48 l 9.72 2.39 l 3.89 4.31 l 3.4 1.44 l 4.86 5.74 l -3.4 4.79 l 0.97 3.35 l -2.43 2.39 l -0.97 3.83 h -3.4 l -2.43 -3.83 l -1.46 2.87 h -2.92 l -1.46 5.27 l 1.94 1.91 l -2.43 0.48 l -2.92 4.79 l -0.97 2.87 l -1.46 2.87 l 1.94 2.39 l -2.92 0.48 h -1.94 v -2.39 l -6.32 0.48 v 3.83 h -2.43 l -0.49 4.79 l 2.92 1.44 l -1.94 1.91 v 4.31 l 2.43 1.44 l 2.92 2.39 l -0.49 3.35 l -3.4 0.48 l -2.92 -2.87 l -2.43 -1.44 v -1.92 l -3.4 -3.35 h -2.92 l -0.97 -3.35 l -5.35 -2.87 l 0.49 -2.39 l -0.49 -1.44 l -4.37 1.44 l -2.92 2.39 l -2.43 -2.87 L 19.49 126.38 Z",
  },
  {
    id: 2,
    title: "C",
    path: "M 98.24 55.54 l 2.43 -2.39 l 2.43 0.48 l 2.43 1.91 l -1.94 5.26 l 2.92 0.96 l 2.43 2.87 l 2.92 -0.48 l -0.49 -3.35 l -2.43 -1.44 l 2.92 -3.35 l 5.35 0.48 l 2.43 3.35 l 1.94 2.39 l 1.46 2.39 L 124 63.2 h 4.37 l 0.97 0.96 l 2.92 -1.44 v 3.35 l 0.49 5.74 l 3.4 2.39 l 3.4 1.91 l 0.97 2.39 l -0.49 2.87 l -0.97 7.18 l 1.46 4.79 l 0.97 5.26 l 3.89 3.35 l 4.37 2.87 l 3.4 2.87 l -1.46 3.83 l -1.46 3.35 l -1.46 1.91 l -2.92 -3.35 l -4.86 4.31 l 0.49 5.27 l -3.89 -3.83 l -2.43 4.79 l -7.78 -1.91 l -5.35 1.44 l 1.94 4.79 l -2.43 2.87 l -1.46 2.39 l -4.37 -0.48 l -3.89 -3.35 h -2.43 v -5.26 l -3.89 -0.96 v -11.49 l -1.94 -2.39 l -1.94 -3.35 l -5.35 -1.44 l 0.49 -2.39 l -1.46 -0.96 v -3.83 l -0.49 -1.44 l 1.94 -2.39 l -1.46 2.39 l 2.92 -4.31 l 2.92 -1.44 v -8.14 l 1.46 -2.87 v -1.91 l -0.49 -6.7 l 1.46 -2.87 l -5.35 -5.74 L 98.24 55.54 L 98.24 55.54 L 98.24 55.54 Z",
  }]
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, shrink-to-fit=no"
    />
    <title>Choropleth Map</title>
    <style>
      .region:hover path {
        cursor: pointer;
        fill: #ccc;
      }
      .region:hover text {
        fill: #fff;
      }
    </style>
  </head>

  <body>
    <div ></div>
    
    <!-- Script Section -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </body>
</html>

CodePudding user response:

If you want to follow your current approach, you should calculate the centre based on half the width or height added to the x or y position.

Also, notice that d3.select(nodes[i]).node() is unnecessary, that's the same of just nodes[i].

Here is your code with the changes:

document.addEventListener("DOMContentLoaded", function() {
  const h = 400;
  const w = 400;

  var svg = d3
    .select(".map")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .attr("viewBox", "0 0 400 400")

  var groups = svg
    .selectAll(".groups")
    .data(map)
    .enter()
    .append("g")
    .attr("class", "region");

  groups
    .append("path")
    .attr("d", (m) => m.path)
    .attr("stroke-width", "1")
    .attr("stroke", "#fff")
    .attr("fill", "#333")

  groups
    .append("text")
    .text((m) => m.title)
    .style("font-weight", "bold")
    .style("font-family", "Jersey")
    .attr("fill", "#6D6E71")
    .attr("text-anchor", "middle")
    .style("text-transform", "uppercase")
    .attr("x", (m, i, nodes) => {
      let bbox = nodes[i].previousSibling.getBBox();
      let centreX = bbox.width / 2   bbox.x;
      return centreX;
    })
    .attr("y", (m, i, nodes) => {
      let bbox = nodes[i].previousSibling.getBBox();
      let centreY = bbox.height / 2   bbox.y;
      return centreY;
    });
});

const map = [{
    id: 0,
    title: "A",
    path: "M 45.25 34.96 l -0.97 -3.35 l -0.97 -2.39 l 2.43 -3.35 l 1.94 -4.31 l 1.94 -5.74 l 3.89 -5.74 l 0.97 6.22 l 0.49 3.35 l 3.89 3.83 l 3.89 -0.96 l 0.97 3.35 l 3.89 0.96 l 0.49 2.87 l 3.89 1.44 l 0.49 1.91 L 76.36 34 l 2.43 -2.87 l 1.46 3.83 l 1.94 2.39 l 2.92 1.91 l 0.97 1.91 l 2.43 -0.96 l 1.46 1.44 l -1.46 3.35 l 1.46 1.44 l 1.94 1.44 v 0.96 l 3.89 -0.48 v 1.91 l 0.97 1.91 v 2.39 l 2.43 1.91 l -0.49 4.31 l 1.94 3.35 l 2.43 3.35 l -0.97 3.35 v 4.79 v 4.31 l -1.46 3.83 l 0.97 4.79 l -2.43 3.35 l -3.4 4.79 l -3.89 -0.96 l -5.35 -0.48 l -1.94 -3.83 l -2.43 -1.91 l 2.92 -5.26 l -1.94 -3.83 l -2.43 -2.87 l -3.89 -1.44 l -1.94 -1.91 l -1.46 -2.87 l -2.43 0.96 c 0 0 -0.61 -0.48 -1.94 -0.96 s -3.4 -0.96 -3.4 -0.96 l -2.43 -0.96 l -2.43 1.91 l -3.89 -0.96 l -3.4 -1.44 l -2.92 1.91 l -0.49 2.39 c 0 0 -1.58 -0.24 -3.4 -0.96 c -1.82 -0.72 -3.89 -1.91 -3.89 -1.91 l -0.97 -1.44 l 3.4 -1.91 l 1.94 -2.39 l -1.46 -2.87 v -6.7 l 5.35 -0.48 l 1.94 -1.91 l 1.94 -2.87 l 4.37 -1.44 l 0.97 -3.35 l 2.43 3.83 l 2.43 -1.44 l 1.94 -3.35 l -1.94 -2.39 l -1.94 -2.87 v -3.35 l -3.4 -2.87 l -4.37 0.96 l -2.43 -3.83 l -2.43 1.91 l -2.92 -0.48 L 45.25 34.96 Z",
  },
  {
    id: 1,
    title: "B",
    path: "M 19.49 126.38 l -2.43 -4.31 l -2.92 -3.83 l 1.94 -4.31 l -4.86 -0.96 l 1.46 -7.66 l 3.4 -2.39 l -0.49 -2.87 l 1.94 -3.83 l 5.83 -3.83 l 1.94 -5.26 l -4.37 -2.39 l 2.43 -4.31 l 0.97 -4.31 l 5.35 -1.91 v -4.79 l 7.29 0.96 l 5.35 -1.44 l 7.29 4.79 l 1.46 -3.83 l 4.96 -1.03 l 4.27 1.51 l 3.89 -0.48 l 9.72 2.39 l 3.89 4.31 l 3.4 1.44 l 4.86 5.74 l -3.4 4.79 l 0.97 3.35 l -2.43 2.39 l -0.97 3.83 h -3.4 l -2.43 -3.83 l -1.46 2.87 h -2.92 l -1.46 5.27 l 1.94 1.91 l -2.43 0.48 l -2.92 4.79 l -0.97 2.87 l -1.46 2.87 l 1.94 2.39 l -2.92 0.48 h -1.94 v -2.39 l -6.32 0.48 v 3.83 h -2.43 l -0.49 4.79 l 2.92 1.44 l -1.94 1.91 v 4.31 l 2.43 1.44 l 2.92 2.39 l -0.49 3.35 l -3.4 0.48 l -2.92 -2.87 l -2.43 -1.44 v -1.92 l -3.4 -3.35 h -2.92 l -0.97 -3.35 l -5.35 -2.87 l 0.49 -2.39 l -0.49 -1.44 l -4.37 1.44 l -2.92 2.39 l -2.43 -2.87 L 19.49 126.38 Z",
  },
  {
    id: 2,
    title: "C",
    path: "M 98.24 55.54 l 2.43 -2.39 l 2.43 0.48 l 2.43 1.91 l -1.94 5.26 l 2.92 0.96 l 2.43 2.87 l 2.92 -0.48 l -0.49 -3.35 l -2.43 -1.44 l 2.92 -3.35 l 5.35 0.48 l 2.43 3.35 l 1.94 2.39 l 1.46 2.39 L 124 63.2 h 4.37 l 0.97 0.96 l 2.92 -1.44 v 3.35 l 0.49 5.74 l 3.4 2.39 l 3.4 1.91 l 0.97 2.39 l -0.49 2.87 l -0.97 7.18 l 1.46 4.79 l 0.97 5.26 l 3.89 3.35 l 4.37 2.87 l 3.4 2.87 l -1.46 3.83 l -1.46 3.35 l -1.46 1.91 l -2.92 -3.35 l -4.86 4.31 l 0.49 5.27 l -3.89 -3.83 l -2.43 4.79 l -7.78 -1.91 l -5.35 1.44 l 1.94 4.79 l -2.43 2.87 l -1.46 2.39 l -4.37 -0.48 l -3.89 -3.35 h -2.43 v -5.26 l -3.89 -0.96 v -11.49 l -1.94 -2.39 l -1.94 -3.35 l -5.35 -1.44 l 0.49 -2.39 l -1.46 -0.96 v -3.83 l -0.49 -1.44 l 1.94 -2.39 l -1.46 2.39 l 2.92 -4.31 l 2.92 -1.44 v -8.14 l 1.46 -2.87 v -1.91 l -0.49 -6.7 l 1.46 -2.87 l -5.35 -5.74 L 98.24 55.54 L 98.24 55.54 L 98.24 55.54 Z",
  }
]
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" />
  <title>Choropleth Map</title>
  <style>
    .region:hover path {
      cursor: pointer;
      fill: #ccc;
    }
    
    .region:hover text {
      fill: #fff;
    }
  </style>
</head>

<body>
  <div ></div>

  <!-- Script Section -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</body>

</html>

However, based on this answer of mine, I'd use the centroid method for geo paths, even though you have only the paths' d attribute. It's just a matter of transforming them.

Here is the code using that approach:

document.addEventListener("DOMContentLoaded", function() {
  const h = 400;
  const w = 400;
  const geoPath = d3.geoPath()
    .projection(d3.geoIdentity());
  const precision = 4;

  var svg = d3
    .select(".map")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .attr("viewBox", "0 0 400 400")

  var groups = svg
    .selectAll(".groups")
    .data(map)
    .enter()
    .append("g")
    .attr("class", "region");

  groups
    .append("path")
    .attr("d", (m) => m.path)
    .each((d, i, n) => {
      const pathLength = n[i].getTotalLength();
      let index = 0;
      const geoJSONObject = {
        "type": "Polygon",
        "coordinates": [
          []
        ]
      };
      while (index < pathLength) {
        const point = n[i].getPointAtLength(index);
        geoJSONObject.coordinates[0].push([point.x, point.y]);
        index  = precision;
      };
      const centroid = geoPath.centroid(geoJSONObject);
      d.centroid = centroid;
    })
    .attr("stroke-width", "1")
    .attr("stroke", "#fff")
    .attr("fill", "#333")

  groups
    .append("text")
    .text((m) => m.title)
    .style("font-weight", "bold")
    .style("font-family", "Jersey")
    .attr("fill", "#6D6E71")
    .attr("text-anchor", "middle")
    .style("text-transform", "uppercase")
    .attr("x", d => d.centroid[0])
    .attr("y", d => d.centroid[1])
});

const map = [{
    id: 0,
    title: "A",
    path: "M 45.25 34.96 l -0.97 -3.35 l -0.97 -2.39 l 2.43 -3.35 l 1.94 -4.31 l 1.94 -5.74 l 3.89 -5.74 l 0.97 6.22 l 0.49 3.35 l 3.89 3.83 l 3.89 -0.96 l 0.97 3.35 l 3.89 0.96 l 0.49 2.87 l 3.89 1.44 l 0.49 1.91 L 76.36 34 l 2.43 -2.87 l 1.46 3.83 l 1.94 2.39 l 2.92 1.91 l 0.97 1.91 l 2.43 -0.96 l 1.46 1.44 l -1.46 3.35 l 1.46 1.44 l 1.94 1.44 v 0.96 l 3.89 -0.48 v 1.91 l 0.97 1.91 v 2.39 l 2.43 1.91 l -0.49 4.31 l 1.94 3.35 l 2.43 3.35 l -0.97 3.35 v 4.79 v 4.31 l -1.46 3.83 l 0.97 4.79 l -2.43 3.35 l -3.4 4.79 l -3.89 -0.96 l -5.35 -0.48 l -1.94 -3.83 l -2.43 -1.91 l 2.92 -5.26 l -1.94 -3.83 l -2.43 -2.87 l -3.89 -1.44 l -1.94 -1.91 l -1.46 -2.87 l -2.43 0.96 c 0 0 -0.61 -0.48 -1.94 -0.96 s -3.4 -0.96 -3.4 -0.96 l -2.43 -0.96 l -2.43 1.91 l -3.89 -0.96 l -3.4 -1.44 l -2.92 1.91 l -0.49 2.39 c 0 0 -1.58 -0.24 -3.4 -0.96 c -1.82 -0.72 -3.89 -1.91 -3.89 -1.91 l -0.97 -1.44 l 3.4 -1.91 l 1.94 -2.39 l -1.46 -2.87 v -6.7 l 5.35 -0.48 l 1.94 -1.91 l 1.94 -2.87 l 4.37 -1.44 l 0.97 -3.35 l 2.43 3.83 l 2.43 -1.44 l 1.94 -3.35 l -1.94 -2.39 l -1.94 -2.87 v -3.35 l -3.4 -2.87 l -4.37 0.96 l -2.43 -3.83 l -2.43 1.91 l -2.92 -0.48 L 45.25 34.96 Z",
  },
  {
    id: 1,
    title: "B",
    path: "M 19.49 126.38 l -2.43 -4.31 l -2.92 -3.83 l 1.94 -4.31 l -4.86 -0.96 l 1.46 -7.66 l 3.4 -2.39 l -0.49 -2.87 l 1.94 -3.83 l 5.83 -3.83 l 1.94 -5.26 l -4.37 -2.39 l 2.43 -4.31 l 0.97 -4.31 l 5.35 -1.91 v -4.79 l 7.29 0.96 l 5.35 -1.44 l 7.29 4.79 l 1.46 -3.83 l 4.96 -1.03 l 4.27 1.51 l 3.89 -0.48 l 9.72 2.39 l 3.89 4.31 l 3.4 1.44 l 4.86 5.74 l -3.4 4.79 l 0.97 3.35 l -2.43 2.39 l -0.97 3.83 h -3.4 l -2.43 -3.83 l -1.46 2.87 h -2.92 l -1.46 5.27 l 1.94 1.91 l -2.43 0.48 l -2.92 4.79 l -0.97 2.87 l -1.46 2.87 l 1.94 2.39 l -2.92 0.48 h -1.94 v -2.39 l -6.32 0.48 v 3.83 h -2.43 l -0.49 4.79 l 2.92 1.44 l -1.94 1.91 v 4.31 l 2.43 1.44 l 2.92 2.39 l -0.49 3.35 l -3.4 0.48 l -2.92 -2.87 l -2.43 -1.44 v -1.92 l -3.4 -3.35 h -2.92 l -0.97 -3.35 l -5.35 -2.87 l 0.49 -2.39 l -0.49 -1.44 l -4.37 1.44 l -2.92 2.39 l -2.43 -2.87 L 19.49 126.38 Z",
  },
  {
    id: 2,
    title: "C",
    path: "M 98.24 55.54 l 2.43 -2.39 l 2.43 0.48 l 2.43 1.91 l -1.94 5.26 l 2.92 0.96 l 2.43 2.87 l 2.92 -0.48 l -0.49 -3.35 l -2.43 -1.44 l 2.92 -3.35 l 5.35 0.48 l 2.43 3.35 l 1.94 2.39 l 1.46 2.39 L 124 63.2 h 4.37 l 0.97 0.96 l 2.92 -1.44 v 3.35 l 0.49 5.74 l 3.4 2.39 l 3.4 1.91 l 0.97 2.39 l -0.49 2.87 l -0.97 7.18 l 1.46 4.79 l 0.97 5.26 l 3.89 3.35 l 4.37 2.87 l 3.4 2.87 l -1.46 3.83 l -1.46 3.35 l -1.46 1.91 l -2.92 -3.35 l -4.86 4.31 l 0.49 5.27 l -3.89 -3.83 l -2.43 4.79 l -7.78 -1.91 l -5.35 1.44 l 1.94 4.79 l -2.43 2.87 l -1.46 2.39 l -4.37 -0.48 l -3.89 -3.35 h -2.43 v -5.26 l -3.89 -0.96 v -11.49 l -1.94 -2.39 l -1.94 -3.35 l -5.35 -1.44 l 0.49 -2.39 l -1.46 -0.96 v -3.83 l -0.49 -1.44 l 1.94 -2.39 l -1.46 2.39 l 2.92 -4.31 l 2.92 -1.44 v -8.14 l 1.46 -2.87 v -1.91 l -0.49 -6.7 l 1.46 -2.87 l -5.35 -5.74 L 98.24 55.54 L 98.24 55.54 L 98.24 55.54 Z",
  }
]
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" />
  <title>Choropleth Map</title>
  <style>
    .region:hover path {
      cursor: pointer;
      fill: #ccc;
    }
    
    .region:hover text {
      fill: #fff;
    }
  </style>
</head>

<body>
  <div ></div>

  <!-- Script Section -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</body>

</html>

  • Related