chart = {
const xmax = 75000
const ymax = xmax + 5000
const data_clipped = data.filter(d=>d['Gross income']<=xmax)
const below_threshold = data_clipped.filter(d=>d['Gross income']<=parameters.threshold)
const above_threshold = data_clipped.filter(d=>d['Gross income']>parameters.threshold)
const negative_tax_points = {}
negative_tax_points.top = {x: parameters.threshold/3,
y: parameters.minimum_income + (parameters.threshold-parameters.minimum_income)/3
}
negative_tax_points.mid = {x: negative_tax_points.top.x,
y:(negative_tax_points.top.y + parameters.threshold/3)/2
}
negative_tax_points.annotation = {x: Math.max(negative_tax_points.top.x, 10000),
y: Math.max(negative_tax_points.top.y + 5000, 20000),
text: ['Below the threshold, the negative income tax supplements base pay']
}
negative_tax_points.annotation.bend = (360/(2*Math.PI)) * Math.atan(
(negative_tax_points.mid.x - negative_tax_points.annotation.x)/
(negative_tax_points.mid.y-negative_tax_points.annotation.y))
const threshold_annotation = { x1: parameters.threshold + 7500,
y1: Math.max(parameters.threshold - 7500, 3000),
x2: parameters.threshold,
y2: parameters.threshold,
text: ['At the threshold, the effective tax rate is zero']
}
threshold_annotation.bend = -(360/(2*Math.PI)) * Math.atan(
(threshold_annotation.y2 - threshold_annotation.y1)/
(threshold_annotation.x2 - threshold_annotation.x1))-10
const label_values = data.filter(d=>d['Gross income'] >= xmax)[0]
return Plot.plot({
x: {domain: [0, xmax]},
y: {domain: [0, ymax]},
width: 640,
// height: 640 * ymax/xmax,
insetRight: 50,
marks: [
Plot.axisX({label: 'Income before tax →',
fontSize: 10,
tickFormat: (d, i, _) => (i === _.length - 1 ? `£${d/1000}k` : d/1000)}),
Plot.axisY({label: '↑ Income after tax',
tickSize: 0,
tickFormat: (d, i, _) => (i === _.length - 1 ? `£${d/1000}k` : d/1000)}),
Plot.gridY({interval: 10000, stroke: dark_grey, strokeDasharray:[2,2], strokeOpacity:.2}),
Plot.ruleY([0]),
Plot.line(below_threshold, {y: 'Net income', x: 'Gross income',
stroke: line_color,
strokeWidth:2.5}),
Plot.line(above_threshold, {y: 'Net income', x: 'Gross income',
stroke: dark_grey,
strokeWidth:2.5}),
Plot.line(data_clipped, {y: 'Gross income', x:'Gross income',
strokeDasharray:[2, 4],
stroke: dark_grey,
strokeWidth: 1}),
Plot.area(below_threshold, {x1: 'Gross income', y1: 'Gross income', x2: 'Gross income', y2: 'Net income',
fill: line_color,
fillOpacity:0.1}),
Plot.area(above_threshold, {x1: 'Gross income', y1: 'Gross income', x2: 'Gross income', y2: 'Net income',
fill: dark_grey,
fillOpacity:0.1}),
// Plot.arrow([{x1:parameters.threshold, y1:0, x2:parameters.threshold, y2:parameters.threshold}],
// {x1: 'x1',
// y1: 'y1',
// x2: 'x2',
// y2: 'y2',
// stroke: dark_grey,
// // strokeDasharray: [2,2],
// strokeOpacity: 0.5,
// strokeWidth:1,
// headLength:0,
// inset: 5,
// bend: false}),
Plot.dot([parameters.threshold], {x:d=>d, y:d=>d, fill:line_color}),
Plot.arrow([threshold_annotation],
{x1: 'x1',
y1: 'y1',
x2: 'x2',
y2: 'y2',
bend: threshold_annotation.bend,
insetEnd: 10,
strokeWidth:1}),
Plot.text([threshold_annotation], {
x: d => d.x1,
y: d => d.y1,
text: d => d.text,
lineWidth: 13,
textAnchor: 'start',
lineAnchor: 'middle',
dx: 5
}),
Plot.arrow([negative_tax_points],
{x1: pts => pts.annotation.x,
y1: pts => pts.annotation.y,
x2: pts=>pts.mid.x,
y2: pts=>pts.mid.y,
markerEnd:'none',
bend: negative_tax_points.annotation.bend,
// insetEnd: 10,
strokeWidth:1,
strokeOpacity:0.8}),
Plot.text([negative_tax_points], {
x: pts => pts.annotation.x,
y: pts => pts.annotation.y,
text: pts => pts.annotation.text,
lineWidth: 15,
lineAnchor: 'bottom',
textAnchor: 'middle',
dy: -10,
}),
Plot.text([label_values],
{
x: d => d['Gross income'],
y: d => d['Net income'],
text: ['Income after tax'],
fontWeight: 'bold',
color: dark_grey,
dx: 10,
dy: 0,
lineAnchor: 'middle',
lineWidth: 5,
textAnchor: 'start',
clip: false
}),
Plot.text([label_values],
{
x: d => d['Gross income'],
y: d => d['Gross income'],
text: ['Base pay'],
fontWeight: 'regular',
color: dark_grey,
dx: 10,
dy: 0,
lineAnchor: 'middle',
lineWidth: 5,
textAnchor: 'start',
clip: false
})
],
marginLeft: 60,
paddingRight: 200
})
}