chart = function(data, month, year) {
const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map(d => Object.create(d));
let regionDomain = []
data.nodes.forEach((row) => {
regionDomain.push(row.id)
});
let radiusFxn = d3[radiusMeasure]().domain(d3.extent(rDomain)).range(rNode)
let strokeFxn = d3[scaleMeasure]().domain(d3.extent(strokeDomain)).range(sWidth)
let colorFxn = d3.scaleOrdinal().domain(regionDomain).range(d3['schemeTableau10']);
let xScale = d3.scaleLinear().domain(longDomain).range([20, width-200])
let yScale = d3.scaleLinear().domain(latDomain).range([height-20, 20])
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(50))
.force("charge", d3.forceManyBody().strength(-1000))
.force('collision', d3.forceCollide().radius(100))
.force("center", d3.forceCenter(width / 2.25, height / 2))
.force('x', d3.forceX((d) => xScale(d.x)).strength(1))
.force('y', d3.forceY((d) => yScale(d.y)).strength(2));
const svg = d3.select(DOM.svg(width, height));
svg.append("text")
.attr("x", (width / 2))
.attr("y", 50)
.attr("text-anchor", "middle")
.style("font-size", "48px")
.attr("font-family", font)
.attr("fill", "#000")
.text(month + ' ' + year);
// build the arrow.
svg.append("svg:defs").selectAll("marker")
.data(["end"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 1)
.attr("markerHeight", 1)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
const link = svg.append("g")
.attr("stroke-opacity", 0.3)
.selectAll("path")
.data(links)
.join("path")
.attr("stroke-width", d => strokeFxn(d[volMeasure]))
.attr("stroke", d => {console.log(d.source.id); return colorFxn(d.source.id)})
.attr("fill", "transparent")
// .attr("marker-end", "url(#end)");
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 6)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", d => radiusFxn(d.dayAve))
.attr("fill", d => colorFxn(d.id))
.call(drag(simulation))
const textElements = svg.append('g')
.selectAll('text')
.data(nodes)
.enter().append('text')
.text(node => node.id)
.attr('font-size', 18)
.attr("font-family", font)
.attr("fill", "#000")
.attr('dx', -20)
.attr('dy', 4)
simulation.on("tick", () => {
link
.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy)*arcness;
if (dx === 0 && dy ===0) {
// Fiddle with this angle to get loop oriented.
const xRotation = -45;
// Needs to be 1.
const largeArc = 1;
let sweep = 0
// Change sweep to change orientation of loop.
if (d.source.id === 'North-East' || d.source.id === 'East' || d.source.id === 'North') {
sweep = 1;
}
// Make drx and dry different to get an ellipse
// instead of a circle.
const drx = 70;
const dry = 70;
const x1 = d.source.x;
const y1 = d.source.y;
// For whatever reason the arc collapses to a point if the beginning
// and ending points of the arc are the same, so kludge it.
const x2 = d.target.x + 1;
const y2 = d.target.y + 1;
return "M" + x1 + "," + y1 + "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " + x2 + "," + y2;
} else {
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}
});
textElements
.attr("x", node => node.x)
.attr("y", node => node.y)
// node
// .attr("cx", d => d.x)
// .attr("cy", d => d.y);
node.attr('transform', (d) => {
return 'translate(' + (d.x) + ',' + (d.y) + ')';
});
});
invalidation.then(() => simulation.stop());
return svg.node();
}