chart = {
const make_annotations = d3_annotation.annotation().notePadding(10).type(type).annotations(annotations)
const svg = d3.create("svg").attr("viewBox", [0, 0, width+margin.left+margin.right, height + margin.top + margin.bottom]).style("background", "#efefe7")
const wrapper = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`).style("background","red")
const annotation_group = wrapper.append("g")
.attr("class", "annotation-group").attr("opacity", 0)
.call(make_annotations)
wrapper.append("g").call(xAxis)
wrapper.append("g").attr('transform', `translate(50,0)`).call(yAxis)
const x_legend = d3.scaleLinear()
.domain(color_scale.domain())
.rangeRound([0, 260]);
const color_legend = wrapper.append('g').style('font-size', "0.8rem").attr("transform", `translate(${width-300}, ${height/3 - 100})`)
const color_legend_label = color_legend.append("text").text("Population Count")
const color_legend_scale = color_legend.append('g').attr("transform", `translate(${0}, ${2})`)
color_legend_scale.selectAll("rect").data(d3.range( d3.extent(population_data)[0], d3.extent(population_data)[1], 1000000)).enter().append('rect').attr('x', d => x_legend(d)).attr("height", 10).attr("width", (260 / 15)).attr("fill", d => color_scale(d))
color_legend_scale.call(
d3.axisBottom(x_legend)
.tickFormat(v => `${parseFloat(v / 1000000).toFixed(0)}M`)
).call(g => g.select('.domain').remove()).call(g => g.selectAll('line').remove()).call(g => g.selectAll('text').attr("transform", `translate(${0}, ${8})`))
color_legend.append("g").attr("transform", `translate(${260/4}, ${50})`)
.call(circleLegend);
let projection = d3.geoMercator().translate([margin.left, margin.top]).fitSize([width, height], phl_regions2)
let geoGenerator = d3.geoPath().projection(projection)
let regions = wrapper.selectAll('region').data(nodes)
let areas = regions.enter().append("path").attr("class", "areas").attr("d", d => geoGenerator(d)).attr("fill", d => color_scale(d.properties.Population))
const simulation = d3.forceSimulation();
simulation.nodes(nodes);
simulation.force("charge", d3.forceManyBody().strength(20))
.force('x', d3.forceX(d => x("All")))
.force('y', d3.forceY().y(d => y(parseFloat(d.properties.GrowthRate))))
.force('collide',d3.forceCollide().radius(function(d) { return d.r + 1}))
simulation.alpha(0.95).tick(5)
simulation.on('tick', () => {
areas.transition().ease(d3.easeLinear).attr('transform', function(d) {
return `translate(${d.x},${ d.y})`})
})
simulation.stop()
let inward = nodes.map((d,i) => {
return flubber.combine(flubber.splitPathString(geoGenerator(d)), circlePath(0,0, d.r),
{ single: true })
})
let outward = nodes.map((d,i) => {
return flubber.separate(circlePath(0,0, d.r), flubber.splitPathString(geoGenerator(d)),
{ single: true })
})
const morph = () => {
annotation_group.transition().delay(3000).ease(d3.easeLinear).attr('opacity', 1)
areas.transition().delay(2000).duration(1000).ease(d3.easeLinear).attr('transform', function(d) {
let node_centroid = geoGenerator.centroid(d.geometry)
return `translate(${ node_centroid[0]},${ node_centroid[1]})`}).attrTween('d', (d,i) => {return inward[i]}).on("end", () => {simulation.alpha(0.95).restart().on("end", changeBack2Map)})
const changeBack2Map = () => {
simulation.stop()
annotation_group.transition().delay(3000).ease(d3.easeLinear).attr('opacity', 0)
areas.transition().delay(3000).duration(1000).ease(d3.easeLinear).attr('transform', function(d) {
return `translate(${0},${0})`}).attrTween('d', (d,i) => {return outward[i]}).on('end', morph)
}
}
morph();
return svg.node()
}