Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tile map for civil penalty fund #8537

Merged
merged 24 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e75d6cf
prog
wpears Aug 20, 2024
8da3e6e
Merge branch 'gp-inv' into tile-map-fixes
wpears Aug 20, 2024
d6da928
tighten up steps and step math
wpears Aug 28, 2024
df52542
Add Spanish homepage prototype
niqjohnson Aug 29, 2024
4ca7965
Make Spanish homepage prototype viewable on DEV5
niqjohnson Aug 29, 2024
81254ff
Update Spanish homepage
niqjohnson Aug 30, 2024
84f4f76
Update Spanish homepage link
niqjohnson Sep 3, 2024
b26c796
Merge branch 'tile-map-fixes' into spanish-homepage
wpears Sep 4, 2024
ca8ea44
Update wallet illustration
niqjohnson Sep 4, 2024
653208c
Update Spanish homepage content
niqjohnson Sep 10, 2024
946f170
Merge branch 'main' into spanish-homepage
niqjohnson Sep 13, 2024
81de8f1
Change eyebrow link for usability testing
niqjohnson Sep 13, 2024
6f2edfc
stakeholder tweaks
wpears Sep 20, 2024
0317f81
perCapita tilemap
wpears Sep 20, 2024
f71ae16
per capita legend
wpears Sep 20, 2024
4eb1b96
Merge branch 'main' into spanish-homepage
niqjohnson Sep 27, 2024
1ca9bc6
Update eyebrow for testing
niqjohnson Sep 27, 2024
644c1fe
Merge branch 'spanish-homepage' into tile-map-fixes
wpears Oct 2, 2024
f220dc9
Merge remote-tracking branch 'origin/main' into spanish-homepage
niqjohnson Oct 7, 2024
b115619
Merge branch 'spanish-homepage' into tile-map-fixes
wpears Oct 7, 2024
eaad1b1
Merge branch 'main' into tile-map-fixes
wpears Oct 15, 2024
97a44f7
Remove population note and add ability to override dataClasses for ti…
wpears Oct 15, 2024
bdd7e7d
Back out spanish homepage merges
wpears Oct 22, 2024
37b7d72
Merge branch 'main' into tile-map-fixes
wpears Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions cfgov/unprocessed/css/on-demand/simple-chart.scss
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
.highcharts-label {
font-family: 'Avenir Next', Arial, sans-serif;
font-size: 12px;
color: '#101820';
color: #101820;
text {
pointer-events: none;
}
Expand All @@ -60,6 +60,9 @@
}
}
}
.highcharts-data-label > span {
font-family: 'Avenir Next', Arial, sans-serif !important;
}
}
.filter-wrapper {
@include u-grid-column(1, 2);
Expand Down Expand Up @@ -236,7 +239,7 @@

.legend-title {
margin-bottom: 10px;
font-size: 14px;
font-size: 16px;
}

.legend-color,
Expand Down
24 changes: 24 additions & 0 deletions cfgov/unprocessed/js/routes/on-demand/simple-chart/chart-hooks.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import populations from './populations.js';

const hooks = {
// Example transform
monotonicY(data) {
Expand All @@ -6,6 +8,28 @@ const hooks = {
y: i + 1,
}));
},
//Includes very janky alignment hack
cpf_formatter() {
let v = this.point.value;
if (this.point.perCapita)
v = this.point.perCapita * populations[this.point.name];
const val = Math.round(v / 1e6);
const digits = Math.ceil(Math.log10(val));
return `<span style="visibility:hidden">${digits > 1 ? (digits > 2 ? 'o.' : 'o') : '.'}</span><span style="font-weight:500;">${
this.point.name
}</span><span style="visibility:hidden">${digits > 1 ? 'o' : ''}</span><br/><span style="font-weight:300">$${val}M</span>`;
},

cpf_labeller() {
let val = this.point.value;
if (this.point.perCapita) val *= populations[this.point.name];
return `<b style="font-size:18px; font-weight:600">${
this.point.label
}</b><br/>Payments to consumers: <b>$${Math.round(val).toLocaleString(
'en-US',
)}</b><br/>Number of consumers: <b>${this.point.consumers.toLocaleString('en-US')}
</b>`;
},

cct_yoy_transform(d) {
return d['Number of Loans'].map((v, i) => {
Expand Down
54 changes: 54 additions & 0 deletions cfgov/unprocessed/js/routes/on-demand/simple-chart/populations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const populations = {
AL: 5024294,
AK: 733374,
AZ: 7157902,
AR: 3011490,
CA: 39538212,
CO: 5773707,
CT: 3605912,
DE: 989946,
DC: 689548,
FL: 21538216,
GA: 10713771,
HI: 1455274,
ID: 1839117,
IL: 12813469,
IN: 6785442,
IA: 3190427,
KS: 2937835,
KY: 4506297,
LA: 4657785,
ME: 1363177,
MD: 6177253,
MA: 7032933,
MI: 10077674,
MN: 5706804,
MS: 2961306,
MO: 6154889,
MT: 1084244,
NE: 1961965,
NV: 3104617,
NH: 1377524,
NJ: 9289039,
NM: 2117525,
NY: 20202320,
NC: 10439459,
ND: 779079,
OH: 11799331,
OK: 3959411,
OR: 4237279,
PA: 13002788,
RI: 1097371,
SC: 5118422,
SD: 886668,
TN: 6910786,
TX: 29145459,
UT: 3271614,
VT: 643077,
VA: 8631373,
WA: 7705267,
WV: 1793713,
WI: 5893713,
WY: 576850,
};
export default populations;
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function makeChartOptions(data, dataAttributes) {
let defaultObj = cloneDeep(getDefaultChartObject(chartType));

if (styleOverrides) {
overrideStyles(styleOverrides, defaultObj, data);
overrideStyles(JSON.parse(styleOverrides), defaultObj, data);
}

if (xAxisSource && chartType !== 'datetime') {
Expand Down
179 changes: 129 additions & 50 deletions cfgov/unprocessed/js/routes/on-demand/simple-chart/tilemap-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import tilemap from 'highcharts/modules/tilemap';
import cloneDeep from 'lodash.clonedeep';
import defaultTilemap from './tilemap-styles.js';
import usLayout from './us-layout.js';
import populations from './populations.js';
import { alignMargin, formatSeries, overrideStyles } from './utils.js';

tilemap(Highmaps);
Expand All @@ -18,8 +19,10 @@ function makeTilemapOptions(data, dataAttributes) {

let defaultObj = cloneDeep(defaultTilemap);

let styles;
if (styleOverrides) {
overrideStyles(styleOverrides, defaultObj, data);
styles = JSON.parse(styleOverrides);
overrideStyles(styles, defaultObj, data);
}

const formattedSeries = formatSeries(data);
Expand All @@ -31,17 +34,19 @@ function makeTilemapOptions(data, dataAttributes) {

defaultObj = {
...defaultObj,
...getMapConfig(formattedSeries),
...getMapConfig(formattedSeries, defaultObj, styles.perCapita),
};

defaultObj.tooltip.formatter = function () {
const label = yAxisLabel ? yAxisLabel + ': ' : '';
return `<span style="font-weight:600">${
this.point.name
}</span><br/>${label}<span style="font-weight:600">${
Math.round(this.point.value * 10) / 10
}</span>`;
};
if (!defaultObj.tooltip.formatter) {
defaultObj.tooltip.formatter = function () {
const label = yAxisLabel ? yAxisLabel + ': ' : '';
return `<span style="font-weight:600">${
this.point.label
}</span><br/>${label}<span style="font-weight:600">${
Math.round(this.point.value * 10) / 10
}</span>`;
};
}

defaultObj.title = { text: undefined };
defaultObj.accessibility.description = description;
Expand All @@ -56,7 +61,7 @@ function makeTilemapOptions(data, dataAttributes) {
* Makes a legend for the tilemap
* @param {object} node - The chart node
* @param {object} data - The data object
* @param {string } legendTitle - The legend title
* @param {string} legendTitle - The legend title
*/
function updateTilemapLegend(node, data, legendTitle) {
const classes = data.colorAxis.dataClasses;
Expand All @@ -82,66 +87,140 @@ function updateTilemapLegend(node, data, legendTitle) {
title.innerText = legendTitle;
legend.appendChild(title);
}
if (data.perCapita) {
labels[0].innerText = 'Less';
labels[labels.length - 1].innerText = 'More';
for (let i = 1; i < labels.length - 1; i++) {
labels[i].innerText = '\xa0';
}
}
colors.forEach((v) => legend.appendChild(v));
labels.forEach((v) => legend.appendChild(v));
}

/**
*
* @param {number} v - A given step min or max
* @returns {Array} - An array with the step adjusted to label with a millions value or not
*/
function mLabel(v) {
if (v >= 1e6) {
return [v / 1e6, 'M'];
}
return [v, ''];
}

/**
*
* @param {number} v - Upper end of a given step
* @returns {number} - The step trimmed so the bins don't overlap
*/
function trimTenth(v) {
return Math.round((v - 0.1) * 10) / 10;
}

/**
*
* @param {number} s1 - step min
* @param {number} s2 - step max
* @param {boolean} isLast - whether we're operating on the last data class
* @returns {string} formatted legend label
*/
function formatLegendValues(s1, s2, isLast) {
const f1 = mLabel(s1);
const f2 = mLabel(s2);
return `$${f1[0]}${f1[1]} - $${isLast ? f2[0] : trimTenth(f2[0])}${f2[1]}`;
}

/**
*
* @param {number} s1 - step min
* @param {number} s2 - step max
* @param {string} color - hex color for legend class
* @param {boolean} isLast - whether we're operating on the last data class
* @returns {object} - dataClass object for highcharts
*/
function makeDataClass(s1, s2, color, isLast = 0) {
return {
from: s1,
to: s2,
color,
name: formatLegendValues(s1, s2, isLast),
};
}

/**
*
* @param {number} v - The raw number to get the divisor for
* @returns {number} a divisor which can round the number to its largest digit
*/
function makeDivisor(v) {
const precision = Math.floor(v).toString().length;
return Math.pow(10, precision - 1);
}

/**
* Generates a config object to be added to the chart config
* @param {Array} series - The formatted series data
* @param {object} defaultObj - The style object with overrides applied
* @param {boolean} perCapita - Whether data should be perCapita
* @returns {Array} series data with a geographic component added
*/
function getMapConfig(series) {
function getMapConfig(series, defaultObj, perCapita) {
let min = Infinity;
let max = -Infinity;
const data = series[0].data;
let dataMin = Infinity;
let data = series[0].data;
if (perCapita) {
data = data.map((v) => {
return {
...v,
perCapita: v.value / populations[v.name],
};
});
}

data.forEach((v) => {
const val = perCapita ? v.perCapita : v.value;
if (val < dataMin) dataMin = val;
});

const added = data.map((v) => {
const val = Math.round(Number(v.value) * 100) / 100;
const val =
Math.round(Number(perCapita ? v.perCapita : v.value) * 100) / 100;
if (val <= min) min = val;
if (val >= max) max = val;
return {
...usLayout[v.name],
state: v.name,
...v,
value: val,
};
});
min = Math.floor(min);
max = Math.ceil(max);
const step = Math.round((max - min) / 5);
const step1 = min + step;
const step2 = step1 + step;
const step3 = step2 + step;
const step4 = step3 + step;
const trimTenth = (v) => Math.round((v - 0.1) * 10) / 10;

const divisor = makeDivisor(min);
min = Math.floor(min / divisor) * divisor;
max = Math.ceil(max / divisor) * divisor;

let step = (max - min) / 5;
const stepDivisor = makeDivisor(step);
step = Math.round(step / stepDivisor) * stepDivisor;

const step1 = Math.round(min + step);
const step2 = Math.round(step1 + step);
const step3 = Math.round(step2 + step);
const step4 = Math.round(step3 + step);

return {
colorAxis: {
dataClasses: [
{
from: min,
to: step1,
color: '#addc91',
name: `${min} - ${trimTenth(step1)}`,
},
{
from: step1,
to: step2,
color: '#e2efd8',
name: `${step1} - ${trimTenth(step2)}`,
},
{
from: step2,
to: step3,
color: '#ffffff',
name: `${step2} - ${trimTenth(step3)}`,
},
{
from: step3,
to: step4,
color: '#d6e8fa',
name: `${step3} - ${trimTenth(step4)}`,
},
{ from: step4, color: '#7eb7e8', name: `${step4} - ${max}` },
],
dataClasses: defaultObj.dataClasses
? defaultObj.dataClasses
: [
makeDataClass(min, step1, '#d4eac6'),
makeDataClass(step1, step2, '#addc91'),
makeDataClass(step2, step3, '#48b753'),
makeDataClass(step3, step4, '#1e9642'),
makeDataClass(step4, max, '#187835', 1),
],
},
series: [{ clip: false, data: added }],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ const tilemap = {
enabled: true,
formatter: function () {
return `<span style="font-weight:500">${
this.point.state
this.point.name
}</span><br/><span style="font-weight:300">${Math.round(
this.point.value,
)}</span>`;
},
style: {
textOutline: false,
fontSize: 14,
fontSize: '13px',
},
},
},
Expand All @@ -46,6 +46,7 @@ const tilemap = {
style: {
fontFamily: 'Avenir Next',
fontSize: '16px',
lineHeight: '24px',
},
},
legend: {
Expand Down
Loading
Loading