I'm trying to display an icon in the center of each path
element but it doesn't look right
My code simply calculated the center point based on the width & height of the path
const center = {
x: (bbox.x - svg_box.x) + bbox.width / 2,
y: (bbox.y - svg_box.y) + bbox.height / 2,
}
Can this be improved using a centroid function? Or using d3?
I could not figure out how to find the centroid of an existing path using d3.
Thank you
D3 has two centroid methods: arc.centroid
and path.centroid
(from d3-geo), and none will work with path elements like you have here.
However, we can use path.centroid
for getting the centroids of those paths, but it's quite hacky: you have to create a geoJSON object based on your actual path just to pass that object to path.centroid
. Therefore, you'd be better creating your own.
That said, let's see how that approach works. We can iterate over each path, getting its length and setting a dummy geoJSON object:
const pathLength = n[i].getTotalLength();
let index = 0;
const geoJSONObject = {
"type": "Polygon",
"coordinates": [
[]
]
};
Then, we move along the path and populate the geoJSON object (here 400/1237 is just a quick way to calculate the viewport values, you can use a proper matrix if you want)...
while (index < pathLength) {
const point = n[i].getPointAtLength(index);
geoJSONObject.coordinates[0].push([point.x * (400 / 1237), point.y * (400 / 1232)]);
index += precision;
};
...and finally we pass that object to path.centroid
:
const centroid = path.centroid(geoJSONObject);
Here's the snippet with that solution:
const controls = d3.select(".controls"),
path = d3.geoPath()
.projection(d3.geoIdentity()),
precision = 100;
d3.selectAll("path").each((_, 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 * (400 / 1237), point.y * (400 / 1232)]);
index += precision;
};
const centroid = path.centroid(geoJSONObject);
controls.append("div")
.style("left", centroid[0] + "px")
.style("top", centroid[1] + "px");
})
.container {
position: relative;
display: inline-flex;
}
path {
outline: 1px solid #0F0;
}
.controls {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.controls>div {
position: absolute;
width: 5px;
height: 5px;
background-color: red;
}
<script src="https://d3js.org/d3.v7.min.js"></script>
<div class="container">
<div class="controls"></div>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" x="0px" y="0px" viewBox="0 0 1237 1232" style="enable-background:new 0 0 1237 1232;" xml:space="preserve">
<style type="text/css">
.st1 {
fill: none;
stroke: #000000;
stroke-miterlimit: 10;
}
</style>
<g>
<path class="st1" d="M1036.3,1040.8C893.1,896.6,750.5,753,607.8,609.4c0.1-0.4,0.3-0.8,0.4-1.2c3.4,0.4,6.9,0.8,10.3,1.4
c37.9,6.1,75.9,12.3,113.8,18.5c40.4,6.5,80.8,13.1,121.2,19.6c40.2,6.5,80.5,13,120.7,19.5c39.9,6.5,79.8,12.9,119.7,19.4
c36.5,5.9,72.9,11.8,109.4,17.8c1.8,0.3,3.9,1.3,4.8,2.6c0.5,0.7-1,3.2-2.1,4.5c-35.1,42-67.4,86.1-92.8,134.8
c-20.4,39.2-36.2,80.4-51.4,121.9c-8.4,23-16.1,46.2-24.1,69.3C1037.4,1038.2,1037,1039.1,1036.3,1040.8z" />
<path class="st1" d="M604.1,609.4c0.9,5.3,1.9,10.6,2.7,15.9c3.6,23.1,7.2,46.2,10.7,69.3c3.1,20.5,6.2,41,9.4,61.4
c3.5,22.4,7.1,44.9,10.5,67.3c3.2,20.5,6.3,41,9.4,61.4c3.2,20.5,6.4,40.9,9.6,61.4c2.8,18.2,5.6,36.4,8.4,54.6
c3.5,22.6,7,45.2,10.5,67.8c3.2,20.5,6.3,40.9,9.5,61.4c3.1,20.3,6.3,40.6,9.4,60.9c0.7,4.7,1.8,9.5,2.3,14.2
c0.2,1.8,0.2,4.5-0.9,5.5c-0.9,0.8-3.7,0.2-5.3-0.4c-43.3-17.2-87-33.3-131.7-46.7c-31.9-9.5-64.4-14.5-97.8-15.8
c-45-1.8-89.9,0.3-135.9,2.9c92.9-180.7,185.4-360.9,278-541.1C603.4,609.4,603.8,609.4,604.1,609.4z" />
<path class="st1" d="M511.8,1.5c31.1,200.5,62,400.4,93,600.2c-0.3,0.2-0.6,0.4-0.9,0.6c-2.2-2.1-4.5-4.2-6.7-6.3
c-26.4-26.6-52.8-53.3-79.2-79.9c-22.7-22.8-45.4-45.6-68.1-68.4c-49.7-50-99.3-100-149-150c-36.8-37-73.5-74-110.3-111
c-3.8-3.8-7.5-7.5-11.1-11.5c-1.3-1.5-2.1-3.5-3.1-5.2c1.7-0.8,3.4-1.9,5.2-2.3c21.6-4.8,43.3-9,64.7-14.4
c44.9-11.3,89.2-24.6,130.9-44.9c39-19,72.8-45.5,103.9-75.5C491.4,22.9,501.1,12.4,511.8,1.5z" />
<path class="st1" d="M600.9,606.3c-10.5,5.3-21,10.6-31.5,16c-69.1,34.9-138.3,69.8-207.4,104.7c-62.6,31.6-125.2,63.2-187.7,94.9
c-36.4,18.4-72.8,36.9-109.2,55.3c-0.9,0.5-1.7,1.1-2.7,1.3c-1.4,0.4-2.8,0.5-4.2,0.7c-0.2-1.6-0.9-3.2-0.6-4.6
c1.6-9.2,3.8-18.2,4.9-27.5c2.3-20.2,4.6-40.3,5.7-60.6c1.8-30.3,0.1-60.6-4-90.7c-5.7-41.4-15.8-81.6-31.6-120.3
C23.7,554,13,533.3,3.2,512.2c-0.5-1-1-2-2.1-4.3c200.5,32.5,400.1,64.8,599.6,97C600.7,605.4,600.8,605.9,600.9,606.3z" />
<path class="st1" d="M1150.2,329.6c-180,90.5-359.5,180.8-540.4,271.8c1.2-2.8,1.6-4.3,2.3-5.7c30.1-58.7,60.3-117.3,90.4-176
c22.5-43.9,45-87.7,67.5-131.6c37.8-73.6,75.6-147.3,113.5-220.8c0.8-1.6,2.5-2.7,3.8-4.1c1.2,1.5,2.8,2.7,3.7,4.4
c24.3,46.6,51.6,91.3,84.4,132.6c33.1,41.6,74.4,73.3,119.8,99.9c16.9,9.9,34.6,18.6,51.9,27.8
C1148.1,328.4,1148.9,328.9,1150.2,329.6z" />
</g>
</svg>
</div>