populationChart = {
const svg = d3
.create('svg')
.attr('viewBox', [0, 0, width, height])
.attr('font-family', 'Roboto Condensed, Helvetica Neue, Hiragino Sans');
const populationX = d3
.scaleLinear()
.domain([0, 600000])
.range([0, sexX.bandwidth()]);
const g = svg.append('g');
g.append('rect')
.classed('bg', true)
.attr('width', width)
.attr('height', height)
.attr('fill', 'hsl(42, 100%, 98%)');
const startDate = new Date('2020-08-16');
const endDate = new Date('2020-08-22');
const data = ageSexPatientsOfDateRange(startDate, endDate);
const ageG = g
.selectAll('g.age')
.data(data)
.join('g')
.classed('age', true)
.attr('transform', d => `translate(0, ${ageY(d.key)})`);
const sexG = ageG
.selectAll('g')
.data(d => d.values)
.join('g')
.attr('transform', d => `translate(${sexX(d.key)}, 0)`);
sexG
.selectAll('rect.bar')
.data(d => [d])
.join('rect')
.classed('bar', true)
.attr('x', d =>
d.key === '男性' ? sexX.bandwidth() - populationX(d.population) : 0
)
.attr('y', 0)
.attr('width', d => populationX(d.population))
.attr('height', ageY.bandwidth())
.attr('fill', 'hsl(19, 100%, 90%)')
.attr('opacity', 0.5);
const rowNum = 6;
const circleMargin = (ageY.bandwidth() - rowNum) * 0.05;
const r = (ageY.bandwidth() - (rowNum + 1) * circleMargin) / rowNum / 2;
const circleX = (d, i) => {
let x = Math.floor(i / rowNum) * (r * 2 + circleMargin) + r + circleMargin;
if (d.sex === '男性') {
x = sexX.bandwidth() - x;
}
if (d.sex === '調査中') {
x = width / 2;
}
return x;
};
const circleY = (d, i) => {
if (d.sex === '調査中') {
i = i + 4;
}
return (i % rowNum) * (r * 2 + circleMargin) + r + circleMargin;
};
const circleTransform = (d, i) => {
let transform;
switch (d.sex) {
case '男性':
transform = `translate(${sexX.bandwidth() -
(Math.floor(i / rowNum) * (r * 2 + circleMargin) +
r +
circleMargin)},
${(i % rowNum) * (r * 2 + circleMargin) + r + circleMargin})`;
break;
case '女性':
transform = `translate(${Math.floor(i / rowNum) *
(r * 2 + circleMargin) +
r +
circleMargin},
${(i % rowNum) * (r * 2 + circleMargin) + r + circleMargin})`;
break;
case '調査中':
default:
transform = `translate(${width / 2 -
r +
Math.floor(i / rowNum) * (r * 2 + 1) +
r +
circleMargin},
${((i + 4) % rowNum) * (r * 2 + circleMargin) + r + circleMargin})`;
break;
}
return transform;
};
const patientStroke = '#000';
const patientStrokeWidth = d => (d.statusSort <= 6 ? 1 : 0.5);
sexG
.selectAll('circle')
.data(d => d.values)
.join('circle')
.attr('class', d => `status-${transitionIndex(d)}`)
.attr('r', r)
// .attr('r', 0)
.attr('fill', patientColor)
.attr('stroke', patientStroke)
.attr('stroke-width', patientStrokeWidth)
.attr('stroke-opacity', 0.75)
.attr('transform', circleTransform);
const lastIndex = d => Math.ceil(d.values.length / rowNum) * rowNum;
// sexG
// .append('text')
// .classed('num-patients', true)
// .attr('transform', (d, i) => {
// return `translate(${circleX(d.values[0], lastIndex(d))},
// ${circleY(d.values[0], lastIndex(d)) + 16 * 1.8})`;
// })
// .attr('opacity', d => (d.values[0].sex === '調査中' ? 0 : .75))
// .attr('text-anchor', d => (d.values[0].sex === '男性' ? 'end' : 'start'))
// .selectAll('tspan')
// .data(d =>
// [
// `${d.rate === Infinity ? '' : d3.format('.2f')(d.rate)}`,
// `${d.values.length}人`
// ].map(
// (e, i) =>
// `${e}${
// d.key === '女性' && d.values[0].age === 80
// ? i === 0
// ? '(10万人あたり)'
// : '(実数)'
// : ''
// }`
// )
// )
// .join('tspan')
// .attr('x', (d, i) => `0em`)
// .attr('dx', (d, i) => `0em`)
// .attr('dy', (d, i) => `${i * 1.2}em`)
// .attr('font-size', (d, i) => (i === 0 ? 16 : 12))
// .attr('font-weight', (d, i) => (i === 0 ? 600 : 300))
// .text(d => d);
g.call(addAgeLabels);
g.selectAll('text.sex')
.data(ageSexPatients[0].values)
.join('text')
.classed('sex', true)
.text(d => d.key)
.attr('text-anchor', d => (d.key === '男性' ? 'end' : 'start'))
.attr('font-family', 'sans-serif')
.attr('font-size', '16px')
.attr('font-weight', 'bold')
.attr('transform', d =>
d.key === '男性'
? `translate(${sexX(d.key) + sexX.bandwidth()}, ${margin.top})`
: `translate(${sexX(d.key)}, ${margin.top})`
);
const status = g
.append('g')
.selectAll('g')
.data(statuses.map(d => ({ status: d, statusSort: statusSort(d) })))
.join('g')
.attr('class', d => `status-${transitionIndex(d)}`)
.attr(
'transform',
(d, i) => `translate(${margin.left / 2}, ${margin.top + 40 + i * 20})`
);
// .attr('opacity', 0);
status
.append('circle')
.attr('cx', r)
.attr('cy', r)
.attr('r', r)
.attr('fill', patientColor)
.attr('stroke', patientStroke)
.attr('stroke-width', patientStrokeWidth)
.attr('stroke-opacity', 0.75);
status
.append('text')
.text(d => d.status)
.attr('font-family', 'sans-serif')
.attr('font-size', '12px')
.attr('dy', '8px')
.attr('transform', `translate(${r * 2 + circleMargin}, 0)`);
const texts = [
`集計期間:${d3.timeFormat('%Y/%m/%d')(startDate)} – ${d3.timeFormat(
'%Y/%m/%d'
)(endDate)} (7日間)`,
'データ',
`患者数:大阪府(2020年${updateDate.getMonth() +
1}月${updateDate.getDate()}日更新)`,
' 人口:総務省統計局人口推計(2018年10月)',
// ' 正規|非正規雇用労働者:平成29年就業構造基本調査',
// ' 就業者数:総務省統計局労働力調査(2019年平均)',
// `府発表${d3.format(',')(patients.length)}人のうち居住地大阪府外${
// cityPatients.find(d => d.key == '大阪府外').values.length
// }人を除く`,
`作図:SUGIMOTO Tatsuo`
];
g.append('text')
.text('大阪府 新型コロナウイルス感染確認者の年代性別')
.attr('transform', `translate(${margin.left / 2}, ${margin.top / 2})`)
.attr('font-family', 'Hiragino Sans')
.attr('font-size', '30px')
.attr('font-weight', '300')
.attr('letter-spacing', '.1em');
g.append('rect')
.attr('x', margin.left / 2)
.attr('y', margin.top / 2 + 28 + 3 * 18 - 10)
.attr('width', 24)
.attr('height', 12)
.attr('fill', 'hsl(19, 100%, 90%)')
.attr('opacity', 0.5);
// g.append('rect')
// .attr('x', margin.left / 2)
// .attr('y', margin.top / 2 + 28 + 3 * 18 - 10)
// .attr('width', 12)
// .attr('height', 12)
// .attr('fill', 'hsl(220, 100%, 80%)')
// .attr('opacity', 0.5);
// g.append('rect')
// .attr('x', margin.left / 2 + 12)
// .attr('y', margin.top / 2 + 28 + 3 * 18 - 10)
// .attr('width', 12)
// .attr('height', 12)
// .attr('fill', 'hsl(220, 100%, 90%)')
// .attr('opacity', 0.5);
const description = g
.append('g')
.selectAll('text.desc')
.data(texts)
.join('text')
.classed('desc', true)
.text(d => d)
.attr(
'transform',
(d, i) => `translate(${margin.left / 2}, ${margin.top / 2 + 28 + i * 18})`
)
.attr('font-family', 'sans-serif')
.attr('font-size', '12px')
.attr('opacity', 0.8);
// for (let i = 0; i < 5; i++) {
// sexG
// .selectAll(`circle.status-${i}`)
// .transition()
// .delay((d, j) => 5000 + i * 3000 + j * 10)
// .attr('r', r);
// g.selectAll(`g.status-${i}`)
// .transition()
// .delay((d, j) => 5000 + i * 3000 + j * 10)
// .attr('opacity', 1);
// }
return svg.node();
}