map = {
const circle = d3.geoCircle(),
height = width * (9 / 16),
svg = d3
.create("svg")
.attr("width", width)
.attr("height", height);
const path = d3.geoPath().projection(projection);
const topg = svg.append("g").attr("class", "topg");
topg
.selectAll("path")
.data(land.features)
.enter()
.append("path")
.attr("d", path)
.attr("fill", "none")
.attr("stroke", "#999")
.attr("stroke-width", "0.5px");
topg
.append("g")
.selectAll("circle")
.data(plottable_probes)
.enter()
.append("a")
.attr("xlink:href", d => `https://atlas.ripe.net/probes/${d.id}`)
.attr("target", d => "_blank")
.append("circle")
.attr("stroke", "none")
.attr("stroke-width", "0.5px")
.attr("cx", d => projection(
[ d.geometry.coordinates[0], d.geometry.coordinates[1] ]
)[0] )
.attr("cy", d => projection(
[ d.geometry.coordinates[0], d.geometry.coordinates[1] ]
)[1] )
.attr("r", d => 2)
.on("mouseover", d => {
mutable hover = d;
})
.on("mouseout", () => {
mutable hover = null;
});
const zoom = d3
.zoom()
.scaleExtent([1, 32])
.on('zoom', () => {
const { transform } = d3.event;
topg.attr('transform', transform);
topg.selectAll("circle").attr('r', 2 / transform.k);
topg.selectAll("d").attr('stroke-width', .5 / transform.k);
topg.selectAll("path").attr("stroke-width", .5 / transform.k);
});
svg.call(zoom);
return Object.assign(svg.node(), {
zoomIn: () => svg.transition().call(zoom.scaleBy, 2),
zoomOut: () => svg.transition().call(zoom.scaleBy, 0.5)
});
}