Есть несколько способов нарисовать эти круги, и более адекватный зависит от того, что вы хотите с ними сделать.
Например, если вы не хотите связывать какие-либо (значимые) данные с этими кругами, вы можете использовать each
с выбором прямоугольников, чтобы получить угол каждого прямоугольника и добавить окружность (к SVG). , а не к прямоугольнику, что невозможно).
Итак, для каждого прямоугольника мы получаем координаты для четырех окружностей ...
var circlePositions = [
[d.x, d.y],
[d.x + d.width, d.y],
[d.x, d.y + d.height],
[d.x + d.width, d.y + d.height]
];
... и добавить их:
svg.selectAll(null)
.data(circlePositions)
.enter()
.append("circle")
.attr("cx", d => d[0])
.attr("cy", d => d[1])
Вот демонстрация с поддельными данными для 3 прямоугольников:
var data = [{x: 20, y: 20, width: 100, height: 20},
{x: 200, y: 80, width: 50, height: 120},
{x: 360, y: 50, width: 120, height: 80}];
var svg = d3.select("svg");
var rects = svg.selectAll(null)
.data(data)
.enter()
.append("rect")
.attr('x', d => d.x)
.attr('y', d => d.y)
.attr('width', d => d.width)
.attr('height', d => d.height)
.style('fill', 'gray')
.style('stroke', 'black');
rects.each(d => {
var circlePositions = [
[d.x, d.y],
[d.x + d.width, d.y],
[d.x, d.y + d.height],
[d.x + d.width, d.y + d.height]
];
svg.selectAll(null)
.data(circlePositions)
.enter()
.append("circle")
.attr("cx", d => d[0])
.attr("cy", d => d[1])
.attr("r", 6)
.attr("fill", "limegreen")
.attr("stroke", "black")
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>