score:1

Accepted answer

I first thought this could be achieved with relative units, but the changing aspect ratio of the SVG gets you into hot waters. So the best approach seems to come with clamping the SVG viewBox to the original image dimensions. These need to be known beforehand, as SVGImageElement is not able to extract them from the image source itself.

The price to pay for this is that the overlay circles have to be resized every time the window is resized.

This example does not concern itself with the drag functionality.

//an event counter
var counter = 0;

//image metadata
var pData = {
    url: "https://m.bmw.de/content/dam/bmw/common/all-models/m-series/x6m/2014/model-card/X6-M-F86_ModelCard.png",
    width: 890,
    height: 501
}

//Create the SVG with viewBox at native image size
var svg = d3.select("body").append("svg")
    .attr("xmlns:xlink", "http://www.w3.org/1999/xlink")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr('viewBox', "0 0 " + pData.width + " " + pData. height)
    .attr("preserveAspectRatio", "xMidyMid")
    .on("click", click);

var defs = svg.append("defs");

//Add a Background-Picture
var pPic = d3.select("body").select("svg").append("image")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("xlink:href", pData.url)

function click() {
    // Ignore the click event if it was suppressed
    if (d3.event.defaultPrevented) return;

    // Extract the click location relative to SVG
    var point = d3.mouse(this);
    // get SVG scaling
    var ctm = svg.node().getScreenCTM(),
        scale = "scale(" + (1 / ctm.a) + "," + (1 / ctm.d) + ")";

    // Unique id
    var id = "dot" + counter++;

    //Append the group offscreen
    var newGroup = defs.append("g")
        .attr("id", id)
        .attr("transform", scale);

    //Append the circle
    var newCircle = newGroup.append("circle")
        .attr("r", "25")
        .attr("class", "dot")
        .style("stroke", "#999999")
        .style("fill", "#66B132")
        .attr("opacity", 0.8);

    //Append the text
    var newText = newGroup.append("text")
        .text("43")
        .style("fill", "#FFFFFF")
        .style("font-family", "Arial")
        .style("font-size", "24px")
        .style("text-anchor", "middle")
        .style("alignment-baseline", "central")
        .style("readonly", "true");

    // indirect rendering with a new viewport
    svg.append("use")
        .attr("xlink:href", "#" + id)
        .attr("x", point[0])
        .attr("y", point[1]);
}

// adjust group sizes on window resize
var resize;
window.addEventListener("resize", function() {
    clearTimeout(resize);
    resize = setTimeout(function () {
        var ctm = svg.node().getScreenCTM();
        // select all groups before they are repositioned
        defs.selectAll('g').attr("transform", "scale(" + (1 / ctm.a) + "," + (1 / ctm.d) + ")");
    }, 100);
});

Related Query

More Query from same tag