Merge branch 'usds:main' into main

This commit is contained in:
Saran Ahluwalia 2021-12-16 06:16:43 -05:00 committed by GitHub
commit ec342a290e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
146 changed files with 6885 additions and 12026 deletions

8
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/**"
schedule:
interval: "daily"
allow:
- dependency-type: "production"

View file

@ -1,10 +1,11 @@
Feature: All links on About page are functional
Scenario: Visitors can learn more about the J40 EO
Given I am on the "About" page
When I look for the "The Justice40 Initiative" CTA
And I click on the "The Justice40 Initiative" "external" link
Then the link should respond successfully
# Todo: Cypress failure - Commenting out due to failure in CI/CD pipeline
# Scenario: Visitors can learn more about the J40 EO
# Given I am on the "About" page
# When I look for the "Justice40 Initiative" CTA
# And I click on the "Justice40 Initiative" "external" link
# Then the link should respond successfully
Scenario: Federal program officer can find and click on their CTA
Given I am on the "About" page

View file

@ -4,8 +4,3 @@ Feature: All links on the dataset cards should be functional
Given I am on the "Methodology" page
When I look for the "Datasets used in methodology" CTA
Then All links under "Datasets used in methodology" should work
Scenario: If I click on any link in the additional indicators dataset, they should work
Given I am on the "Methodology" page
When I look for the "Additional Indicators" CTA
Then All links under "Additional Indicators" should work

View file

@ -9,25 +9,25 @@ describe('Does the map zoom and adjust to lat/long correctly?', () => {
cy.get('.mapboxgl-ctrl-icon.mapboxgl-ctrl-zoom-in').click({force: true});
cy.url().should('include', '#4');
});
it('should show the correct lat/lng coordinates in the URL',
{
retries: {
runMode: 3,
openMode: 3,
},
defaultCommandTimeout: 4000,
execTimeout: 10000,
taskTimeout: 10000,
pageLoadTimeout: 10000,
requestTimeout: 5000,
responseTimeout: 10000,
},
() => {
cy.getMap().then((map) => {
cy.panTo(map, [-77.9, 35.04]);
cy.url().should('include', '#4/35.04/-77.9');
});
});
// it('should show the correct lat/lng coordinates in the URL',
// {
// retries: {
// runMode: 3,
// openMode: 3,
// },
// defaultCommandTimeout: 4000,
// execTimeout: 10000,
// taskTimeout: 10000,
// pageLoadTimeout: 10000,
// requestTimeout: 5000,
// responseTimeout: 10000,
// },
// () => {
// cy.getMap().then((map) => {
// cy.panTo(map, [-77.9, 35.04]);
// cy.url().should('include', '#4/35.04/-77.9');
// });
// });
// This test hangs intermittently (30% of the time) need to investigate why
// it('allows user to specify alternative starting URL',

View file

@ -1,30 +1,19 @@
@use '../../styles/design-system.scss' as *;
@import "../utils.scss";
$sidePanelLabelFontColor: #171716;
@mixin sidePanelLabelStyle {
font-size: small;
font-size: medium;
color: $sidePanelLabelFontColor;
font-weight: 600;
}
@mixin categorizationCircleStyle {
height: 0.6rem;
width: 0.6rem;
border-radius: 100%;
align-self: center;
margin-top: 2rem;
margin-right: 0.5rem;
opacity: 0.6;
}
@mixin indicatorBox {
display: flex;
flex-direction: column;
padding: 0 1rem 1.5rem;
&:last-child {
border-bottom: none;
}
.versionInfo {
padding: .5rem 1rem .5rem 1.2rem;
font-size: medium;
font-weight: bold;
border-bottom: 1px solid $sidePanelBorderColor;
}
.areaDetailContainer {
@ -39,14 +28,25 @@ $sidePanelLabelFontColor: #171716;
align-items: center;
padding-bottom: 2rem;
.isInFocus {
padding: .5rem 1rem .25rem 1.2rem;
font-size: small;
font-weight: bold;
@include u-margin-top(2);
}
.communityOfFocus {
display: flex;
.communityOfFocusCircle {
@include categorizationCircleStyle;
background: #1a4480;
h3 {
margin-top: 0;
margin-bottom: 0;
}
}
.feedbackLink {
font-size: small;
@include u-margin-top(1);
}
}
@ -64,12 +64,10 @@ $sidePanelLabelFontColor: #171716;
}
.censusText {
font-size: small;
font-size: medium;
}
}
//Divider styles
.divider {
@include sidePanelLabelStyle;
@ -79,45 +77,3 @@ $sidePanelLabelFontColor: #171716;
border-top: 1px solid $sidePanelBorderColor;
border-bottom: 1px solid $sidePanelBorderColor;
}
//Indicator box styles
.indicatorBoxMain {
@include indicatorBox;
border-bottom: $sidePanelBorder;
}
.indicatorBoxAdditional {
@include indicatorBox;
border-bottom: 1px solid #1b1b1b;
background-color: $additionalCardsBGColor;
}
.indicatorRow {
display: flex;
@media screen and (max-width: $mobileBreakpoint) {
flex: 1 0 40%;
align-self: inherit;
padding-left: 3rem;
padding-top: 1rem;
}
.indicatorName {
flex: 0 1 77%;
}
.indicatorValue {
margin-top: 1.2rem;
margin-left: 2.2rem;
.indicatorSuperscript {
top: -0.2em
}
}
.indicatorDesc {
max-width: 10rem;
}
}

View file

@ -8,13 +8,9 @@ declare namespace MapModuleScssNamespace {
censusLabel:string;
censusText: string;
divider:string;
indicatorBoxMain:string;
indicatorBoxAdditional:string;
indicatorRow:string;
indicatorName:string;
indicatorValue:string;
indicatorSuperscript:string;
indicatorDesc:string;
feedbackLink:string;
isInFocus:string;
versionInfo: string;
}
}

View file

@ -1,46 +1,32 @@
/* eslint-disable quotes */
// External Libs:
import React, {useEffect} from 'react';
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {Accordion} from '@trussworks/react-uswds';
// Components:
// import {Accordion} from '@trussworks/react-uswds';
import Category from '../Category';
import DisadvantageDot from '../DisadvantageDot';
import Indicator from '../Indicator';
// Styles and constants
import * as styles from './areaDetail.module.scss';
import * as constants from '../../data/constants';
import * as EXPLORE_COPY from '../../data/copy/explore';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
export const readablePercentile = (percentile: number | null) => {
return percentile ? Math.round(percentile * 100) : 'N/A';
};
// Todo: Add internationalization to superscript ticket #582
const getSuperscriptOrdinal = (percentile: number | string) => {
if (typeof percentile === "number") {
const englishOrdinalRules = new Intl.PluralRules('en', {
type: 'ordinal',
});
const suffixes = {
zero: 'th',
one: 'st',
two: 'nd',
few: 'rd',
many: 'th',
other: 'th',
};
return suffixes[englishOrdinalRules.select(percentile)];
}
};
interface IAreaDetailProps {
properties: constants.J40Properties,
}
export interface indicatorInfo {
label: string,
description: string,
value: number,
isDisadvagtaged: boolean,
}
const AreaDetail = ({properties}:IAreaDetailProps) => {
const intl = useIntl();
const [isCommunityFocus, setIsCommunityFocus] = React.useState<boolean>(true);
console.log("Area Detail properies: ", properties);
@ -50,122 +36,298 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
const countyName = properties[constants.COUNTY_NAME] ? properties[constants.COUNTY_NAME] : "N/A";
const stateName = properties[constants.STATE_NAME] ? properties[constants.STATE_NAME] : "N/A";
useEffect(() => {
if (score >= constants.SCORE_BOUNDARY_PRIORITIZED ) {
setIsCommunityFocus(true);
} else {
setIsCommunityFocus(false);
}
}, [score]);
const isCommunityFocus = score >= constants.SCORE_BOUNDARY_PRIORITIZED;
// const sidePanelFeedbackHref = `
// mailto:screeningtool.feedback@usds.gov?subject=Feedback on Census Tract: ${blockGroup}
// `;
interface indicatorInfo {
label: string,
description: string,
value: number,
}
// Define each indicator in the side panel with constants from copy file (for intl)
// Indicators are grouped by category
const expAgLoss:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EXP_AG_LOSS),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_AG_LOSS),
value: properties[constants.EXP_AGRICULTURE_LOSS_PERCENTILE] ?
properties[constants.EXP_AGRICULTURE_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_AGR_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_AGR_LOSS_AND_IS_LOW_INCOME] : null,
};
const expBldLoss:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EXP_BLD_LOSS),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_BLD_LOSS),
value: properties[constants.EXP_BUILDING_LOSS_PERCENTILE] ?
properties[constants.EXP_BUILDING_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_BLD_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_BLD_LOSS_AND_IS_LOW_INCOME] : null,
};
const expPopLoss:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EXP_POP_LOSS),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_POP_LOSS),
value: properties[constants.EXP_POPULATION_LOSS_PERCENTILE] ?
properties[constants.EXP_POPULATION_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_POP_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_POP_LOSS_AND_IS_LOW_INCOME] : null,
};
const lowInc:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LOW_INCOME),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LOW_INCOME),
value: properties[constants.POVERTY_BELOW_200_PERCENTILE] ?
properties[constants.POVERTY_BELOW_200_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_FEDERAL_POVERTY_LEVEL_200] ?
properties[constants.IS_FEDERAL_POVERTY_LEVEL_200] : null,
};
// Todo: Ticket #367 will be replacing descriptions with YAML file
const areaMedianIncome:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.AREA_MEDIAN_INCOME),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.AREA_MEDIAN_INCOME),
value: properties[constants.AREA_MEDIAN_INCOME_PERCENTILE] ?
properties[constants.AREA_MEDIAN_INCOME_PERCENTILE] : null,
};
const eduInfo:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EDUCATION),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EDUCATION),
value: properties[constants.EDUCATION_PROPERTY_PERCENTILE] ?
properties[constants.EDUCATION_PROPERTY_PERCENTILE] : null,
};
const poverty:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.POVERTY),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.POVERTY),
value: properties[constants.POVERTY_PROPERTY_PERCENTILE] ?
properties[constants.POVERTY_PROPERTY_PERCENTILE] : null,
};
const asthma:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.ASTHMA),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.ASTHMA),
value: properties[constants.ASTHMA_PERCENTILE] ?
properties[constants.ASTHMA_PERCENTILE] : null,
};
const diabetes:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIABETES),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIABETES),
value: properties[constants.DIABETES_PERCENTILE] ?
properties[constants.DIABETES_PERCENTILE] : null,
};
const dieselPartMatter:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIESEL_PARTICULATE_MATTER),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIESEL_PARTICULATE_MATTER),
value: properties[constants.DIESEL_MATTER_PERCENTILE] ?
properties[constants.DIESEL_MATTER_PERCENTILE] : null,
};
const lifeExpect:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LIFE_EXPECT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LIFE_EXPECT),
value: properties[constants.LIFE_PERCENTILE] ?
properties[constants.LIFE_PERCENTILE] : null,
};
const energyBurden:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.ENERGY_BURDEN),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.ENERGY_BURDEN),
value: properties[constants.ENERGY_PERCENTILE] ?
properties[constants.ENERGY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_ENERGY_BURDEN_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_ENERGY_BURDEN_AND_IS_LOW_INCOME] : null,
};
const pm25:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PM_2_5),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PM_2_5),
value: properties[constants.PM25_PERCENTILE] ?
properties[constants.PM25_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_PM25_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_PM25_AND_IS_LOW_INCOME] : null,
};
const leadPaint:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LEAD_PAINT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LEAD_PAINT),
value: properties[constants.LEAD_PAINT_PERCENTILE] ?
properties[constants.LEAD_PAINT_PERCENTILE] : null,
const dieselPartMatter:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIESEL_PARTICULATE_MATTER),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIESEL_PARTICULATE_MATTER),
value: properties[constants.DIESEL_MATTER_PERCENTILE] ?
properties[constants.DIESEL_MATTER_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_DIESEL_PM_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_DIESEL_PM_AND_IS_LOW_INCOME] : null,
};
const trafficVolume:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.TRAFFIC_VOLUME),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.TRAFFIC_VOLUME),
value: properties[constants.TRAFFIC_PERCENTILE] ?
properties[constants.TRAFFIC_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_TRAFFIC_PROX_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_TRAFFIC_PROX_AND_IS_LOW_INCOME] : null,
};
const houseBurden:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HOUSE_BURDEN),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HOUSE_BURDEN),
value: properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] ?
properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HOUSE_BURDEN_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HOUSE_BURDEN_AND_IS_LOW_INCOME] : null,
};
const leadPaint:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LEAD_PAINT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LEAD_PAINT),
value: properties[constants.LEAD_PAINT_PERCENTILE] ?
properties[constants.LEAD_PAINT_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LEAD_PAINT_AND_MEDIAN_HOME_VAL_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LEAD_PAINT_AND_MEDIAN_HOME_VAL_AND_IS_LOW_INCOME] : null,
};
// const medHomeVal:indicatorInfo = {
// label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.MED_HOME_VAL),
// description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.MED_HOME_VAL),
// value: properties[constants.MEDIAN_HOME_VALUE_PERCENTILE] ?
// properties[constants.MEDIAN_HOME_VALUE_PERCENTILE] : null,
// isDisadvagtaged: false, // TODO
// };
const proxHaz:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PROX_HAZ),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_HAZ),
value: properties[constants.PROXIMITY_TSDF_SITES_PERCENTILE] ?
properties[constants.PROXIMITY_TSDF_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HAZARD_WASTE_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HAZARD_WASTE_AND_IS_LOW_INCOME] : null,
};
const proxNPL:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PROX_NPL),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_NPL),
value: properties[constants.PROXIMITY_NPL_SITES_PERCENTILE] ?
properties[constants.PROXIMITY_NPL_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_SUPERFUND_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_SUPERFUND_AND_IS_LOW_INCOME] : null,
};
const proxRMP:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PROX_RMP),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_RMP),
value: properties[constants.PROXIMITY_RMP_SITES_PERCENTILE] ?
properties[constants.PROXIMITY_RMP_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_RMP_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_RMP_AND_IS_LOW_INCOME] : null,
};
const wasteWater:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.WASTE_WATER),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.WASTE_WATER),
value: properties[constants.WASTEWATER_PERCENTILE] ?
properties[constants.WASTEWATER_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_WASTEWATER_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_WASTEWATER_AND_IS_LOW_INCOME] : null,
};
const femaRisk:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.FEMA_RISK),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.FEMA_RISK),
value: properties[constants.FEMA_PERCENTILE] ?
properties[constants.FEMA_PERCENTILE] : null,
const asthma:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.ASTHMA),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.ASTHMA),
value: properties[constants.ASTHMA_PERCENTILE] ?
properties[constants.ASTHMA_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_ASTHMA_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_ASTHMA_AND_IS_LOW_INCOME] : null,
};
const diabetes:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIABETES),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIABETES),
value: properties[constants.DIABETES_PERCENTILE] ?
properties[constants.DIABETES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_DIABETES_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_DIABETES_AND_IS_LOW_INCOME] : null,
};
const heartDisease:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HEART_DISEASE),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HEART_DISEASE),
value: properties[constants.HEART_PERCENTILE] ?
properties[constants.HEART_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HEART_DISEASE_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HEART_DISEASE_AND_IS_LOW_INCOME] : null,
};
const houseBurden:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HOUSE_BURDEN),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HOUSE_BURDEN),
value: properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] ?
properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] : null,
const lifeExpect:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LIFE_EXPECT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LOW_LIFE_EXPECT),
value: properties[constants.LIFE_PERCENTILE] ?
properties[constants.LIFE_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LOW_LIFE_EXP_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LOW_LIFE_EXP_AND_IS_LOW_INCOME] : null,
};
const lowMedInc:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LOW_MED_INC),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LOW_MED_INCOME),
value: properties[constants.LOW_MEDIAN_INCOME_PERCENTILE] ?
properties[constants.LOW_MEDIAN_INCOME_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LOW_MEDIAN_INCOME_AND_LOW_HIGH_SCHOOL_EDU] ?
properties[constants.IS_GTE_90_LOW_MEDIAN_INCOME_AND_LOW_HIGH_SCHOOL_EDU] : null,
};
const lingIso:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LING_ISO),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LING_ISO),
value: properties[constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE] ?
properties[constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LINGUISITIC_ISO_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LINGUISITIC_ISO_AND_IS_LOW_INCOME] : null,
};
const unemploy:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.UNEMPLOY),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.UNEMPLOY),
value: properties[constants.UNEMPLOYMENT_PROPERTY_PERCENTILE] ?
properties[constants.UNEMPLOYMENT_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_UNEMPLOYMENT_AND_LOW_HIGH_SCHOOL_EDU] ?
properties[constants.IS_GTE_90_UNEMPLOYMENT_AND_LOW_HIGH_SCHOOL_EDU] : null,
};
const poverty:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.POVERTY),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.POVERTY),
value: properties[constants.POVERTY_PROPERTY_PERCENTILE] ?
properties[constants.POVERTY_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] ?
properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] : null,
};
// const highSchool:indicatorInfo = {
// label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HIGH_SCL),
// description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HIGH_SKL),
// value: properties[constants.HIGH_SCHOOL_PROPERTY_PERCENTILE] ?
// properties[constants.HIGH_SCHOOL_PROPERTY_PERCENTILE] : null,
// isDisadvagtaged: properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] ?
// properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] : null,
// };
const indicators = [areaMedianIncome, eduInfo, poverty];
const additionalIndicators = [
asthma, diabetes, dieselPartMatter, energyBurden, femaRisk, heartDisease,
houseBurden, leadPaint, lifeExpect, pm25, trafficVolume, wasteWater,
// Aggregate indicators based on categories
const categories = [
{
id: 'climate-change',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLIMATE),
indicators: [expAgLoss, expBldLoss, expPopLoss, lowInc],
isDisadvagtaged: properties[constants.IS_CLIMATE_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_CLIMATE_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'clean-energy',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_ENERGY),
indicators: [energyBurden, pm25, lowInc],
isDisadvagtaged: properties[constants.IS_ENERGY_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_ENERGY_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'clean-transport',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_TRANSPORT),
indicators: [dieselPartMatter, trafficVolume, lowInc],
isDisadvagtaged: properties[constants.IS_TRANSPORT_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_TRANSPORT_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'sustain-house',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.SUSTAIN_HOUSE),
indicators: [houseBurden, leadPaint, lowInc],
isDisadvagtaged: properties[constants.IS_HOUSING_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_HOUSING_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'leg-pollute',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.LEG_POLLUTE),
indicators: [proxHaz, proxNPL, proxRMP, lowInc],
isDisadvagtaged: properties[constants.IS_POLLUTION_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_POLLUTION_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'clean-water',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_WATER),
indicators: [wasteWater, lowInc],
isDisadvagtaged: properties[constants.IS_WATER_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_WATER_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'health-burdens',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.HEALTH_BURDEN),
indicators: [asthma, diabetes, heartDisease, lifeExpect, lowInc],
isDisadvagtaged: properties[constants.IS_HEALTH_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_HEALTH_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'work-dev',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.WORK_DEV),
indicators: [lowMedInc, lingIso, unemploy, poverty],
isDisadvagtaged: properties[constants.IS_WORKFORCE_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_WORKFORCE_FACTOR_DISADVANTAGED_L] : null,
},
];
// Create the AccoridionItems by mapping over the categories array. In this array we define the
// various indicators for a specific category. This is an array which then maps over the <Indicator />
// component to render the actual Indicator
const categoryItems = categories.map((category) => ({
id: category.id,
title: <Category name={category.titleText} isDisadvantaged={category.isDisadvagtaged}/>,
content: (
<>
{category.indicators.map((indicator:any, index:number) => {
return <Indicator key={`ind${index}`} indicator={indicator}/>;
})}
</>
),
expanded: false,
}));
return (
<aside className={styles.areaDetailContainer} data-cy={'aside'}>
{/* Methodology version */}
<div className={styles.versionInfo}>
{EXPLORE_COPY.SIDE_PANEL_VERION.TITLE}
</div>
{/* Census Info */}
<ul className={styles.censusRow}>
<li>
<span className={styles.censusLabel}>
@ -192,139 +354,27 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
<span className={styles.censusText}>{` ${population.toLocaleString()}`}</span>
</li>
</ul>
{/* Disadvantaged? */}
<div className={styles.categorization}>
<div className={styles.isInFocus}>
{EXPLORE_COPY.COMMUNITY.IS_FOCUS}
</div>
<div className={styles.communityOfFocus}>
{isCommunityFocus ?
<>
<div className={styles.communityOfFocusCircle} />
<h3>{EXPLORE_COPY.COMMUNITY.OF_FOCUS}</h3>
<DisadvantageDot isDisadvantaged={isCommunityFocus}/>
</> :
<h3>{EXPLORE_COPY.COMMUNITY.NOT_OF_FOCUS}</h3>
}
</div>
<p className={"secondary"}>version {METHODOLOGY_COPY.VERSION_NUMBER}</p>
</div>
<div className={styles.divider}>
<h6>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.INDICATOR_COLUMN_HEADER)}
</h6>
<h6>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PERCENTILE_COLUMN_HEADER)}
</h6>
{/* eslint-disable-next-line max-len */}
{/* <a className={styles.feedbackLink} href={sidePanelFeedbackHref}>{EXPLORE_COPY.COMMUNITY.SEND_FEEDBACK}</a> */}
</div>
<>
{
indicators.map((indicator:any, index:number) => {
return <li key={`ind${index}`} className={styles.indicatorBoxMain} data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
<>
{
additionalIndicators.map((indicator:any, index:number) => {
return <li
key={`ind${index}`}
className={styles.indicatorBoxAdditional}
data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
{/* Temporarily remove Accordions and may place back in later, removed unused
className prop as as styles are based on the id of the Accordion Item */}
{/* <Accordion
multiselectable={true}
items={
[
{
id: 'prioritization-indicators',
title: 'Indicators',
content: (
<>
{
indicators.map((indicator:any, index:number) => {
return <li key={`ind${index}`} className={styles.indicatorBoxMain} data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
),
expanded: true,
},
{
id: 'additional-indicators',
title: 'Additional indicators (not used in prioritization)',
content: (
(
<>
{
additionalIndicators.map((indicator:any, index:number) => {
return <li
key={`ind${index}`}
className={styles.indicatorBoxAdditional}
data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
)
),
expanded: true,
},
]
}/> */}
{/* All category accordions in this component */}
<Accordion multiselectable={true} items={categoryItems}/>
</aside>
);

View file

@ -5,10 +5,13 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
<aside
data-cy="aside"
>
<div>
Methodology version 0.1
</div>
<ul>
<li>
<span>
Census block group:
Census tract:
</span>
<span>
98729374234
@ -41,76 +44,110 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</ul>
<div>
<div>
<div />
Identified as disadvantaged?
</div>
<div>
<h3>
Community of focus
YES
</h3>
<div />
</div>
<p
class="secondary"
</div>
<div
aria-multiselectable="true"
class="usa-accordion"
data-testid="accordion"
>
<h2
class="usa-accordion__heading"
>
<button
aria-controls="climate-change"
aria-expanded="false"
class="usa-accordion__button"
data-testid="accordionButton_climate-change"
type="button"
>
version 0.1
</p>
</div>
<div>
<h6>
Indicator
</h6>
<h6>
Percentile (0-100)
</h6>
<div>
Climate change
</div>
<div
class=""
/>
</div>
</button>
</h2>
<div
class="usa-accordion__content usa-prose"
data-testid="accordionItem_climate-change"
hidden=""
id="climate-change"
>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Area Median Income
</h4>
<div>
19
Expected agriculture loss rate
<div>
Economic loss rate to agriculture resulting from natural hazards
</div>
</div>
<div>
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Median income of the census block group calculated as a percent of the metropolitan areas or state's median income
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Education, less than high school
</h4>
<div>
98
Expected building loss rate
<div>
Economic loss rate to buildings resulting from natural hazards
</div>
</div>
<div>
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Percent of people age 25 or older that didnt get a high school diploma
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Poverty
</h4>
<div>
Expected population loss rate
<div>
Economic loss rate to the population in fatalities and injuries resulting from natural hazards
</div>
</div>
<div>
N/A
<sup>
<span />
</sup>
</div>
</div>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Low income
<div>
Household income is less than or equal to twice the federal poverty level
</div>
</div>
<div>
12
<sup>
@ -120,19 +157,44 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Percent of a block group's population in households where the household income is at or below 100% of the federal poverty level
</p>
</li>
</div>
<h2
class="usa-accordion__heading"
>
<button
aria-controls="clean-energy"
aria-expanded="false"
class="usa-accordion__button"
data-testid="accordionButton_clean-energy"
type="button"
>
<div>
<div>
Clean, efficient energy
</div>
<div
class=""
/>
</div>
</button>
</h2>
<div
class="usa-accordion__content usa-prose"
data-testid="accordionItem_clean-energy"
hidden=""
id="clean-energy"
>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Asthma
</h4>
<div>
Energy cost burden
<div>
Energy costs divided by household income
</div>
</div>
<div>
N/A
<sup>
@ -140,19 +202,17 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
People who answer “yes” to both of the questions: “Have you ever been told by a doctor nurse, or other health professional that you have asthma?” and “Do you still have asthma?"
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Diabetes
</h4>
<div>
PM2.5
<div>
Fine inhalable particles, 2.5 micrometers and smaller
</div>
</div>
<div>
N/A
<sup>
@ -160,19 +220,64 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
People ages 18 and up who report having been told by a doctor, nurse, or other health professionals that they have diabetes other than diabetes during pregnancy
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
<div>
Low income
<div>
Household income is less than or equal to twice the federal poverty level
</div>
</div>
<div>
12
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
</div>
<h2
class="usa-accordion__heading"
>
<button
aria-controls="clean-transport"
aria-expanded="false"
class="usa-accordion__button"
data-testid="accordionButton_clean-transport"
type="button"
>
<div>
<div>
Clean transportation
</div>
<div
class=""
/>
</div>
</button>
</h2>
<div
class="usa-accordion__content usa-prose"
data-testid="accordionItem_clean-transport"
hidden=""
id="clean-transport"
>
<li
data-cy="indicatorBox"
>
<div>
<div>
Diesel particulate matter
</h4>
<div>
Diesel exhaust in the air
</div>
</div>
<div>
N/A
<sup>
@ -180,19 +285,17 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Mixture of particles that is part of diesel exhaust in the air
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Energy burden
</h4>
<div>
Traffic
<div>
Count of vehicles at major roads within 500 meters
</div>
</div>
<div>
N/A
<sup>
@ -200,59 +303,64 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Average annual energy cost ($) divided by household income
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
FEMA Risk Index
</h4>
<div>
N/A
Low income
<div>
Household income is less than or equal to twice the federal poverty level
</div>
</div>
<div>
12
<sup>
<span />
<span>
th
</span>
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Expected Annual Loss Score, which is the average economic loss in dollars resulting from natural hazards each year.
</p>
</li>
</div>
<h2
class="usa-accordion__heading"
>
<button
aria-controls="sustain-house"
aria-expanded="false"
class="usa-accordion__button"
data-testid="accordionButton_sustain-house"
type="button"
>
<div>
<div>
Sustainable housing
</div>
<div
class=""
/>
</div>
</button>
</h2>
<div
class="usa-accordion__content usa-prose"
data-testid="accordionItem_sustain-house"
hidden=""
id="sustain-house"
>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Heart disease
</h4>
<div>
N/A
<sup>
<span />
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
People ages 18 and up who report ever having been told by a doctor, nurse, or other health professionals that they had angina or coronary heart disease
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Housing cost burden
</h4>
<div>
Low income households spending more than 30% of income on housing
</div>
</div>
<div>
95
<sup>
@ -262,19 +370,17 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Households that are low income and spend more than 30% of their income on housing costs
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
<div>
Lead paint
</h4>
<div>
Pre-1960 housing
</div>
</div>
<div>
N/A
<sup>
@ -282,19 +388,64 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Housing units built pre-1960, used as an indicator of potential lead paint exposure in homes
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Life expectancy
</h4>
<div>
Low income
<div>
Household income is less than or equal to twice the federal poverty level
</div>
</div>
<div>
12
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
</div>
<h2
class="usa-accordion__heading"
>
<button
aria-controls="leg-pollute"
aria-expanded="false"
class="usa-accordion__button"
data-testid="accordionButton_leg-pollute"
type="button"
>
<div>
<div>
Legacy pollution
</div>
<div
class=""
/>
</div>
</button>
</h2>
<div
class="usa-accordion__content usa-prose"
data-testid="accordionItem_leg-pollute"
hidden=""
id="leg-pollute"
>
<li
data-cy="indicatorBox"
>
<div>
<div>
Proximity to hazardous waste facilities
<div>
Count of hazardous waste facilities within 5 km
</div>
</div>
<div>
N/A
<sup>
@ -302,19 +453,17 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Estimated years of life expectancy
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
PM2.5
</h4>
<div>
Proximity to NPL sites
<div>
Proposed or listed NPL (Superfund) sites within 5 km
</div>
</div>
<div>
N/A
<sup>
@ -322,19 +471,17 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
Traffic proximity and volume
</h4>
<div>
Proximity to RMP sites
<div>
Risk Management Plan facilities within 5 km
</div>
</div>
<div>
N/A
<sup>
@ -342,19 +489,64 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Count of vehicles (average annual daily traffic) at major roads within 500 meters, divided by distance in meters
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<h4>
<div>
Low income
<div>
Household income is less than or equal to twice the federal poverty level
</div>
</div>
<div>
12
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
</div>
<h2
class="usa-accordion__heading"
>
<button
aria-controls="clean-water"
aria-expanded="false"
class="usa-accordion__button"
data-testid="accordionButton_clean-water"
type="button"
>
<div>
<div>
Clean water and waste
</div>
<div
class=""
/>
</div>
</button>
</h2>
<div
class="usa-accordion__content usa-prose"
data-testid="accordionItem_clean-water"
hidden=""
id="clean-water"
>
<li
data-cy="indicatorBox"
>
<div>
<div>
Wastewater discharge
</h4>
<div>
Toxic concentrations at stream segments within 500 meters
</div>
</div>
<div>
N/A
<sup>
@ -362,12 +554,255 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
</sup>
</div>
</div>
<p
class="secondary j40-indicator"
>
Toxic concentrations at stream segments within 500 meters divided by distance in kilometers
</p>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Low income
<div>
Household income is less than or equal to twice the federal poverty level
</div>
</div>
<div>
12
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
</div>
<h2
class="usa-accordion__heading"
>
<button
aria-controls="health-burdens"
aria-expanded="false"
class="usa-accordion__button"
data-testid="accordionButton_health-burdens"
type="button"
>
<div>
<div>
Health burdens
</div>
<div
class=""
/>
</div>
</button>
</h2>
<div
class="usa-accordion__content usa-prose"
data-testid="accordionItem_health-burdens"
hidden=""
id="health-burdens"
>
<li
data-cy="indicatorBox"
>
<div>
<div>
Asthma
<div>
Number of people who have been told they have asthma
</div>
</div>
<div>
N/A
<sup>
<span />
</sup>
</div>
</div>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Diabetes
<div>
People ages 18 years and older who have diabetes other than diabetes during pregnancy
</div>
</div>
<div>
N/A
<sup>
<span />
</sup>
</div>
</div>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Heart disease
<div>
People ages 18 years and older who have been told they have heart disease
</div>
</div>
<div>
N/A
<sup>
<span />
</sup>
</div>
</div>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Low life expectancy
<div>
Average number of years of life a person can expect to live
</div>
</div>
<div>
N/A
<sup>
<span />
</sup>
</div>
</div>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Low income
<div>
Household income is less than or equal to twice the federal poverty level
</div>
</div>
<div>
12
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
</div>
<h2
class="usa-accordion__heading"
>
<button
aria-controls="work-dev"
aria-expanded="false"
class="usa-accordion__button"
data-testid="accordionButton_work-dev"
type="button"
>
<div>
<div>
Workforce development
</div>
<div
class=""
/>
</div>
</button>
</h2>
<div
class="usa-accordion__content usa-prose"
data-testid="accordionItem_work-dev"
hidden=""
id="work-dev"
>
<li
data-cy="indicatorBox"
>
<div>
<div>
Low median income
<div>
Median income calculated as a percent of the areas median income
</div>
</div>
<div>
N/A
<sup>
<span />
</sup>
</div>
</div>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Linguistic isolation
<div>
Households in which no one age 14 and over speaks English only or also speaks
a language other than English
</div>
</div>
<div>
97
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Unemployment
<div>
Number of unemployed people as a percentage of the labor force
</div>
</div>
<div>
96
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
<li
data-cy="indicatorBox"
>
<div>
<div>
Poverty
<div>
Percent of individuals in households where the household income is at or
below 100% of the federal poverty level
</div>
</div>
<div>
12
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
</div>
</div>
</aside>
</DocumentFragment>
`;

View file

@ -1,6 +1,6 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import AreaDetail, {readablePercentile} from '..';
import AreaDetail from '..';
import {LocalizedComponent} from '../../../test/testHelpers';
import * as constants from '../../../data/constants';
@ -29,9 +29,3 @@ describe('rendering of the AreaDetail', () => {
});
});
describe('tests the readablePercentile function', () => {
expect(readablePercentile(.98)).toEqual(98);
expect(readablePercentile(.07)).toEqual(7);
expect(readablePercentile(.123)).toEqual(12);
expect(readablePercentile(.789)).toEqual(79);
});

View file

@ -0,0 +1,8 @@
@use '../../styles/design-system.scss' as *;
.categoriesContainer {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
@include u-margin-top(4);
}

View file

@ -0,0 +1,12 @@
declare namespace CategoriesNamespace {
export interface ICategoriesScss {
categoriesContainer: string;
}
}
declare const CategoriesScssModule: CategoriesNamespace.ICategoriesScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: CategoriesNamespace.ICategoriesScss;
};
export = CategoriesScssModule;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import Categories from './Categories';
describe('rendering of the Categories', () => {
const {asFragment} = render(
<LocalizedComponent>
<Categories />
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,43 @@
import React from 'react';
import {Grid} from '@trussworks/react-uswds';
import CategoryCard from '../CategoryCard';
import J40MainGridContainer from '../J40MainGridContainer';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
import * as styles from './Categories.module.scss';
const categories = [
METHODOLOGY_COPY.CATEGORIES.CLIMATE_CHANGE,
METHODOLOGY_COPY.CATEGORIES.CLEAN_ENERGY,
METHODOLOGY_COPY.CATEGORIES.CLEAN_TRANSPORT,
METHODOLOGY_COPY.CATEGORIES.AFFORDABLE_HOUSING,
METHODOLOGY_COPY.CATEGORIES.LEGACY_POLLUTION,
METHODOLOGY_COPY.CATEGORIES.CLEAN_WATER,
METHODOLOGY_COPY.CATEGORIES.HEALTH_BURDENS,
METHODOLOGY_COPY.CATEGORIES.WORKFORCE_DEV,
];
const Categories = () => {
return (
<>
<J40MainGridContainer className={styles.categoriesContainer}>
<Grid row>
<Grid col={12}>
<h2>{METHODOLOGY_COPY.CATEGORY.HEADING}</h2>
</Grid>
</Grid>
</J40MainGridContainer>
<J40MainGridContainer className={styles.categoriesContainer}>
{
categories.map((category, index) => <CategoryCard key={index} categoryInfo={category} />)
}
</J40MainGridContainer>
</>
);
};
export default Categories;

View file

@ -0,0 +1,403 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the Categories checks if component renders 1`] = `
<DocumentFragment>
<div
class="grid-container-desktop-lg"
data-testid="gridContainer"
>
<div
class="grid-row"
data-testid="grid"
>
<div
class="grid-col-12"
data-testid="grid"
>
<h2>
Categories
</h2>
</div>
</div>
</div>
<div
class="grid-container-desktop-lg"
data-testid="gridContainer"
>
<div>
<h3>
Climate change
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#exp-agr-loss-rate"
>
expected agriculture loss rate
</a>
OR
<a
href="#exp-bld-loss-rate"
>
expected building loss rate
</a>
OR
<a
href="#exp-pop-loss-rate"
>
expected population loss rate
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Clean energy and energy efficiency
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#energy-burden"
>
energy cost burden score
</a>
OR
<a
href="#pm-25"
>
PM2.5
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Clean transportation
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#diesel-pm"
>
diesel particulate matter
</a>
or
<a
href="#traffic-vol"
>
traffic proximity and volume
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Affordable and sustainable housing
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#lead-paint"
>
lead paint
</a>
AND
<a
href="#median-home"
>
the median home value
</a>
is less than
90th percentile OR at or above the 90th percentile for the
<a
href="#house-burden"
>
housing cost burden
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Reduction and remediation of legacy pollution
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#prox-haz"
>
proximity to hazardous waste facilities
</a>
OR
<a
href="#prox-npl"
>
proximity to NLP sites
</a>
OR
<a
href="#prox-rmp"
>
proximity to RMP sites
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Critical clean water and waste infrastructure
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#waste-water"
>
wastewater discharge
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Health burdens
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#asthma"
>
asthma
</a>
OR
<a
href="#diabetes"
>
diabetes
</a>
OR
<a
href="#heart-disease"
>
heart disease
</a>
OR
<a
href="#life-exp"
>
low life expectancy
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Training and workforce development
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#low-med-inc"
>
low median income relative to area median income
</a>
OR
at or above the 90th percentile for
<a
href="#ling-iso"
>
linguistic isolation
</a>
OR
<a
href="#unemploy"
>
unemployment
</a>
OR
for percentage individuals in households at or below 100% federal
<a
href="#poverty"
>
poverty
</a>
level at or above 90%
</p>
<p>
<strong>
AND
</strong>
where
<a
href="#high-school"
>
the high school degree achievement rates
</a>
for adults 25 years and older is less than 90%
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
</div>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import Categories from './Categories';
export default Categories;

View file

@ -0,0 +1,6 @@
@use '../../styles/design-system.scss' as *;
.categoryContainer {
display: flex;
justify-content: space-between;
}

View file

@ -0,0 +1,13 @@
declare namespace CategoryNamespace {
export interface ICategoryScss {
categoryContainer: string;
disadvantageDot: string;
}
}
declare const CategoryScssModule: CategoryNamespace.ICategoryScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: CategoryNamespace.ICategoryScss;
};
export = CategoryScssModule;

View file

@ -0,0 +1,28 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import Category from './Category';
describe('rendering of the Category disadvantaged', () => {
const {asFragment} = render(
<LocalizedComponent>
<Category name={'test name'} isDisadvantaged={true}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});
describe('rendering of the Category non-disadvantaged', () => {
const {asFragment} = render(
<LocalizedComponent>
<Category name={'test name'} isDisadvantaged={false}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,22 @@
import React from 'react';
import DisadvantageDot from '../DisadvantageDot';
import * as styles from './Category.module.scss';
interface ICategory {
name: string;
isDisadvantaged: boolean;
}
const Category = ({name, isDisadvantaged}:ICategory) => {
return (
<div className={styles.categoryContainer}>
<div>
{name}
</div>
<DisadvantageDot isDisadvantaged={isDisadvantaged}/>
</div>
);
};
export default Category;

View file

@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the Category disadvantaged checks if component renders 1`] = `
<DocumentFragment>
<div>
<div>
test name
</div>
<div />
</div>
</DocumentFragment>
`;
exports[`rendering of the Category non-disadvantaged checks if component renders 1`] = `<DocumentFragment />`;

View file

@ -0,0 +1,3 @@
import Category from './Category';
export default Category;

View file

@ -0,0 +1,15 @@
@use '../../styles/design-system.scss' as *;
@mixin baseCard {
@include u-padding-top(0);
@include u-padding-right(6);
@include u-padding-bottom(6);
@include u-padding-left(6);
@include u-margin-bottom(6);
max-width: 34rem;
}
.categoryCard {
@include baseCard;
@include u-bg('blue-cool-5');
}

View file

@ -0,0 +1,12 @@
declare namespace IndicatorCategoryNamespace {
export interface IIndicatorCategoryScss {
categoryCard: string;
}
}
declare const IndicatorCategoryScssModule: IndicatorCategoryNamespace.IIndicatorCategoryScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: IndicatorCategoryNamespace.IIndicatorCategoryScss;
};
export = IndicatorCategoryScssModule;

View file

@ -0,0 +1,18 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import CategoryCard from './CategoryCard';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
describe('rendering of the CategoryCard', () => {
const {asFragment} = render(
<LocalizedComponent>
<CategoryCard categoryInfo={METHODOLOGY_COPY.CATEGORIES.CLIMATE_CHANGE}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,32 @@
import React from 'react';
import * as styles from './CategoryCard.module.scss';
interface ICategoryInterface {
categoryInfo: {
TITLE: JSX.Element,
IF: JSX.Element,
AND: JSX.Element,
THEN: JSX.Element
}
}
const CategoryCard = ({categoryInfo}: ICategoryInterface) => {
return (
<div className={styles.categoryCard}>
<h3>
{categoryInfo.TITLE}
</h3>
<p>
{categoryInfo.IF}
</p>
<p>
{categoryInfo.AND}
</p>
<p>
{categoryInfo.THEN}
</p>
</div>
);
};
export default CategoryCard;

View file

@ -0,0 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the CategoryCard checks if component renders 1`] = `
<DocumentFragment>
<div>
<h3>
Climate change
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#exp-agr-loss-rate"
>
expected agriculture loss rate
</a>
OR
<a
href="#exp-bld-loss-rate"
>
expected building loss rate
</a>
OR
<a
href="#exp-pop-loss-rate"
>
expected population loss rate
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import CategoryCard from './CategoryCard';
export default CategoryCard;

View file

@ -1,3 +1,4 @@
@use '../../styles/design-system.scss' as *;
@import '../utils.scss';
@mixin baseCard {
@ -8,7 +9,7 @@
.datasetCard {
@include baseCard;
background-color: white;
@include u-bg("gray-3");
}
.datasetCardAdditional {

View file

@ -5,15 +5,16 @@ import * as styles from './datasetCard.module.scss';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
interface IDatasetCardProps {
datasetCardProps: { [key:string]: string }
additionalIndicator: boolean
datasetCardProps: {
[key:string]: string
}
}
const DatasetCard = ({datasetCardProps, additionalIndicator}:IDatasetCardProps) => {
const DatasetCard = ({datasetCardProps}:IDatasetCardProps) => {
const intl = useIntl();
return (
<div className={additionalIndicator ? styles.datasetCardAdditional : styles.datasetCard}>
<div className={styles.datasetCard} id={datasetCardProps.domID}>
<h3 className={styles.datasetCardIndicator}>{datasetCardProps.indicator}</h3>
<div className={styles.datasetCardDescription}>
{datasetCardProps.description}
@ -22,23 +23,23 @@ const DatasetCard = ({datasetCardProps, additionalIndicator}:IDatasetCardProps)
<ul className={styles.datasetCardList}>
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.SOURCE)}
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.RESP_PARTY)}
</span>
<a href={datasetCardProps.dataSourceURL} target={'_blank'} rel="noreferrer">
{datasetCardProps.dataSourceLabel}
{datasetCardProps.respPartyLabel}
</a>
</li>
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.RESOLUTION)}
</span>
{datasetCardProps.dataResolution}
</li>
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.DATE_RANGE)}
</span>
{datasetCardProps.dataDateRange}
{datasetCardProps.dateRange}
</li>
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.USED_IN)}
</span>
{datasetCardProps.usedIn}
</li>
</ul>
</div>

View file

@ -1,41 +1,43 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of addtional indicator dataset card checks if component renders 1`] = `<DocumentFragment />`;
exports[`rendering of indicator dataset card checks if component renders 1`] = `
<DocumentFragment>
<div>
<div
id="low-income"
>
<h3>
Area Median Income
Low Income
</h3>
<div>
Median income of the census block group calculated as a percent
of the metropolitan areas or state's median income.
Percent of a block group's population in households where household income is at or below
200% of the federal poverty level.
</div>
<ul>
<li>
<span>
Data source:
Responsible Party:
</span>
<a
href="https://www.census.gov/programs-surveys/acs"
rel="noreferrer"
target="_blank"
>
Census's American Community Survey
Census's American Community Survey.
</a>
</li>
<li>
<span>
Data resolution:
Date range:
</span>
Census block group
2015-2019
</li>
<li>
<span>
Data date range:
Used in:
</span>
2015-2019
All methodologies except for training and workforce development
</li>
</ul>
</div>

View file

@ -8,19 +8,7 @@ import * as METHODOLOGY_COPY from '../../../data/copy/methodology';
describe('rendering of indicator dataset card', () => {
const {asFragment} = render(
<LocalizedComponent>
<DatasetCard key={0} datasetCardProps={METHODOLOGY_COPY.INDICATORS[0]} additionalIndicator={false}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});
describe('rendering of addtional indicator dataset card', () => {
const {asFragment} = render(
<LocalizedComponent>
<DatasetCard key={0} datasetCardProps={METHODOLOGY_COPY.ADDITIONAL_INDICATORS[0]} additionalIndicator={true}/>
<DatasetCard key={0} datasetCardProps={METHODOLOGY_COPY.INDICATORS[0]}/>
</LocalizedComponent>,
);

View file

@ -1,5 +1,14 @@
@use '../../styles/design-system.scss' as *;
.datasetCardsContainer {
@include u-margin-top(4);
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.returnToTop {
display: flex;
justify-content: flex-end;
@include u-margin-bottom(4);
}

View file

@ -1,6 +1,7 @@
declare namespace DatasetContainerScssNamespace {
export interface IDatasetContainerScss {
datasetCardsContainer: string;
returnToTop: string;
}
}

View file

@ -1,5 +1,5 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {Link, useIntl} from 'gatsby-plugin-intl';
import {Grid} from '@trussworks/react-uswds';
import DatasetCard from '../DatasetCard';
@ -15,7 +15,7 @@ const DatasetContainer = () => {
return (
<>
<J40MainGridContainer fullWidth={true} blueBackground={true}>
<J40MainGridContainer fullWidth={true} blueBackground={false}>
<J40MainGridContainer
dataCy={`${hyphenizeString(METHODOLOGY_COPY.DATASETS.HEADING.defaultMessage)}-block`}>
@ -26,48 +26,25 @@ const DatasetContainer = () => {
</Grid>
<Grid row>
<Grid col={12} tablet={{col: 7}} className={'j40-mb-3'}>
<p>{intl.formatMessage(METHODOLOGY_COPY.DATASETS.INFO)}</p>
</Grid>
</Grid>
<Grid col={12}>
<div className={styles.datasetCardsContainer}>
{METHODOLOGY_COPY.INDICATORS.map((card) => <DatasetCard
key={card.indicator}
datasetCardProps={card}
additionalIndicator={false}
/>)}
</div>
</Grid>
</Grid>
<div className={styles.returnToTop}>
<Link to={`/methodology`}>
{METHODOLOGY_COPY.RETURN_TO_TOP.LINK}
</Link>
</div>
</J40MainGridContainer>
</J40MainGridContainer>
<J40MainGridContainer fullWidth={true} blueBackground={false} >
<J40MainGridContainer
dataCy={`${hyphenizeString(METHODOLOGY_COPY.DATASETS.ADDITIONAL_HEADING.defaultMessage)}-block`}>
<Grid row>
<Grid col={12}>
<h2>{intl.formatMessage(METHODOLOGY_COPY.DATASETS.ADDITIONAL_HEADING)}</h2>
</Grid>
</Grid>
<Grid row>
<Grid col={12} tablet={{col: 7}} className={'j40-mb-3'}>
<p>{intl.formatMessage(METHODOLOGY_COPY.DATASETS.ADDITIONAL_INFO)}</p>
</Grid>
</Grid>
<div className={styles.datasetCardsContainer}>
{METHODOLOGY_COPY.ADDITIONAL_INDICATORS.map((card) => <DatasetCard
key={card.indicator}
datasetCardProps={card}
additionalIndicator={true}
/>)}
</div>
</J40MainGridContainer>
</J40MainGridContainer>
</>
);
};

View file

@ -0,0 +1,19 @@
@use '../../styles/design-system.scss' as *;
.disadvantagedDotSmall {
@include u-circle('105');
@include u-margin-left(1);
align-self: center;
@include u-bg('blue-warm-70v');
}
.disadvantagedDotBig {
@include u-circle(4);
margin: 2.3rem 1.5rem 2rem 0;
opacity: .6;
//Maintain aspect ratio as screen width decreases
flex: 1 0 2rem;
@include u-bg('blue-warm-70v');
}

View file

@ -0,0 +1,14 @@
declare namespace DisadvantagedDotNamespace {
export interface IDisadvantagedDot {
disadvantagedDotBig: string;
disadvantagedDotSmall: string;
}
}
declare const DisadvantagedDotModule: DisadvantagedDotNamespace.IDisadvantagedDot & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: DisadvantagedDotNamespace.IDisadvantagedDot;
};
export = DisadvantagedDotModule;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import DisadvantageDot from './DisadvantageDot';
describe('rendering of the DisadvantageDot disadvantaged', () => {
const {asFragment} = render(
<LocalizedComponent>
<DisadvantageDot isDisadvantaged={true}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,23 @@
import React from 'react';
import * as styles from './DisadvantageDot.module.scss';
interface IDisadvantageDot {
isDisadvantaged?: boolean;
isBig?: boolean;
}
const DisadvantageDot = ({isDisadvantaged = false, isBig}:IDisadvantageDot) => {
let computedClass = '';
if (isBig) {
computedClass = styles.disadvantagedDotBig;
} else {
computedClass = isDisadvantaged ? styles.disadvantagedDotSmall : '';
}
return (
<div className={computedClass} />
);
};
export default DisadvantageDot;

View file

@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the DisadvantageDot disadvantaged checks if component renders 1`] = `
<DocumentFragment>
<div />
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import DisadvantageDot from './DisadvantageDot';
export default DisadvantageDot;

View file

@ -1,4 +1,4 @@
$primary-color: #112f4e;
@use '../../styles/design-system.scss' as *;
.downloadBoxContainer {
@ -6,7 +6,7 @@ $primary-color: #112f4e;
margin: auto;
.downloadBox {
background-color: $primary-color;
@include u-bg('blue-80v');
border-radius: 6px 6px;
.downloadBoxTextBox {
@ -15,12 +15,15 @@ $primary-color: #112f4e;
flex-direction: column;
.downloadBoxTitle {
font-weight: bold;
margin-bottom: 10px;
@include typeset('sans', 'xs', 3);
@include u-text('semibold');
@include u-margin-bottom(2);
}
.downloadBoxText {
margin-bottom: 20px;
@include typeset('sans', 'xs', 3);
@include u-margin-bottom(4);
span {
font-style: italic;
}
@ -42,8 +45,8 @@ $primary-color: #112f4e;
}
.downloadBoxButton{
background-color: white;
color: $primary-color;
@include u-bg('white');
@include u-color('blue-80v');
display: flex;
.downloadPacketText {

View file

@ -2,17 +2,32 @@ import React from 'react';
import {GovBanner} from '@trussworks/react-uswds';
import {useIntl} from 'gatsby-plugin-intl';
// Contexts:
import {useFlags} from '../../contexts/FlagContext';
import Language from '../Language';
import * as styles from './GovernmentBanner.module.scss';
const GovernmentBanner = () => {
const intl = useIntl();
const flags = useFlags();
return (
<div className={styles.fullScreenContainer}>
<div className={styles.bannerContainer}>
{'sp' in flags ? (
<>
<GovBanner language={intl.locale === 'es' ? 'spanish' : 'english'}/>
<Language isDesktop={true}/>
</>
) : (
<>
<GovBanner/>
</>
)}
</div>
</div>
);

View file

@ -135,22 +135,6 @@ exports[`rendering of the GovernmentBanner checks if component renders 1`] = `
</div>
</div>
</section>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
</div>
</DocumentFragment>

View file

@ -7,26 +7,17 @@ exports[`rendering of the HowYouCanHelp checks if various text fields are visibl
How you can help improve the tool
</h2>
<ul>
<li>
If you have helpful information, we would love to
<a
href="mailto:screeningtool.feedback@usds.gov"
>
receive an email from you
</a>
.
</li>
<li>
View our
<a
href="/en/methodology"
>
Data & methodology
Methodology and data
</a>
page and send us feedback.
</li>
<li>
Find your community of interest and
Find communities of interest and
<a
href="mailto:screeningtool.feedback@usds.gov"
>
@ -34,6 +25,15 @@ exports[`rendering of the HowYouCanHelp checks if various text fields are visibl
</a>
.
</li>
<li>
Respond to our request for information on
<a
href="https://www.federalregister.gov/"
>
federalregister.gov
</a>
.
</li>
</ul>
</div>
</DocumentFragment>

View file

@ -0,0 +1,59 @@
@use '../../styles/design-system.scss' as *;
@import "../utils.scss";
@mixin indicator {
display: flex;
flex-direction: column;
@include u-padding-bottom(3);
&:last-child {
border-bottom: none;
@include u-padding-bottom(0);
}
.indicatorRow {
display: flex;
@media screen and (max-width: $mobileBreakpoint) {
flex: 1 0 40%;
align-self: inherit;
padding-left: 3rem;
padding-top: 1rem;
}
.indicatorName {
flex: 0 1 77%;
display: flex;
flex-direction: column;
@include typeset('sans', '2xs', 2);
@include u-text('bold');
.indicatorDesc {
@include typeset('sans', '3xs', 2);
@include u-text('normal');
max-width: 12rem;
@include u-margin-top(0);
@media screen and (max-width: 1024px) {
max-width: 80%;
}
}
}
.indicatorValue {
margin-left: 2.2rem;
.indicatorSuperscript {
top: -0.2em
}
}
}
}
//Indicator box styles
.indicatorBoxMain {
@include indicator;
}
.disadvantagedIndicator {
@include indicator;
@include u-text('blue-warm-70v');
}

View file

@ -0,0 +1,19 @@
declare namespace IndicatorNamespace {
export interface IIndicatorScss {
indicatorBoxMain:string;
indicatorBoxAdditional:string;
indicatorRow:string;
indicatorName:string;
indicatorValue:string;
indicatorSuperscript:string;
indicatorDesc:string;
disadvantagedIndicator:string;
}
}
declare const IndicatorScssModule: IndicatorNamespace.IIndicatorScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: IndicatorNamespace.IIndicatorScss;
};
export = IndicatorScssModule;

View file

@ -0,0 +1,30 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import Indicator, {readablePercentile} from './Indicator';
import {indicatorInfo} from '../AreaDetail';
const highSchool:indicatorInfo = {
label: 'some label',
description: 'some description',
value: 97,
};
describe('rendering of the Indicator', () => {
const {asFragment} = render(
<LocalizedComponent>
<Indicator indicator={highSchool}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});
describe('tests the readablePercentile function', () => {
expect(readablePercentile(.98)).toEqual(98);
expect(readablePercentile(.07)).toEqual(7);
expect(readablePercentile(.123)).toEqual(12);
expect(readablePercentile(.789)).toEqual(79);
});

View file

@ -0,0 +1,55 @@
import React from 'react';
import {indicatorInfo} from '../AreaDetail';
import * as styles from './Indicator.module.scss';
interface IIndicator {
indicator: indicatorInfo,
}
export const readablePercentile = (percentile: number | null) => {
return percentile ? Math.round(percentile * 100) : 'N/A';
};
// Todo: Add internationalization to superscript ticket #582
export const getSuperscriptOrdinal = (percentile: number | string) => {
if (typeof percentile === 'number') {
const englishOrdinalRules = new Intl.PluralRules('en', {
type: 'ordinal',
});
const suffixes = {
zero: 'th',
one: 'st',
two: 'nd',
few: 'rd',
many: 'th',
other: 'th',
};
return suffixes[englishOrdinalRules.select(percentile)];
}
};
const Indicator = ({indicator}:IIndicator) => {
return (
<li
className={indicator.isDisadvagtaged ? styles.disadvantagedIndicator : styles.indicatorBoxMain}
data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<div className={styles.indicatorName}>
{indicator.label}
<div className={styles.indicatorDesc}>
{indicator.description}
</div>
</div>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
</li>
);
};
export default Indicator;

View file

@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the Indicator checks if component renders 1`] = `
<DocumentFragment>
<li
data-cy="indicatorBox"
>
<div>
<div>
some label
<div>
some description
</div>
</div>
<div>
9700
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import Indicator from './Indicator';
export default Indicator;

View file

@ -140,22 +140,6 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
</div>
</div>
</section>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
</div>
<div>
@ -261,7 +245,7 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
data-cy="nav-link-methodology"
href="/en/methodology"
>
Data & methodology
Methodology & data
</a>
</li>
<li
@ -277,24 +261,7 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
<li
class="usa-nav__primary-item"
>
<div>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
<div />
</li>
</ul>
</nav>

View file

@ -157,7 +157,10 @@ const J40Map = ({location}: IJ40Interface) => {
<Grid col={12} desktop={{col: 9}}>
{/*
The MapSearch component is wrapped in a div in order for MapSearch to render correctly in a production build.
The MapSearch component is no longer wrapped in a div in order to allow this feature
to be behind a feature flag. This was causing a bug for MapSearch to render
correctly in a production build. Leaving this comment here in case future flags are
needed in this component
When the MapSearch component is placed behind a feature flag without a div wrapping
MapSearch, the production build will inject CSS due to the null in the false conditional
@ -168,9 +171,7 @@ const J40Map = ({location}: IJ40Interface) => {
to ensure the production build works and that MapSearch and the map (ReactMapGL) render correctly.
*/}
<div>
{'sr' in flags ? <MapSearch goToPlace={goToPlace}/> : null}
</div>
<MapSearch goToPlace={goToPlace}/>
<ReactMapGL
{...viewport}

View file

@ -1,6 +1,9 @@
import React from 'react';
import {IntlContextConsumer, changeLocale} from 'gatsby-plugin-intl';
// Contexts:
import {useFlags} from '../../contexts/FlagContext';
// @ts-ignore
import languageIcon from '/node_modules/uswds/dist/img/usa-icons/language.svg';
import * as styles from './Language.module.scss';
@ -15,7 +18,9 @@ interface ILanguageProps {
}
const Language = ({isDesktop}:ILanguageProps) => {
return (
const flags = useFlags();
return 'sp' in flags ? (
<div className={isDesktop ? styles.languageContainer : styles.languageContainerMobile}>
<img className={styles.languageIcon} src={languageIcon} alt={'language icon for selecting language'}/>
<IntlContextConsumer>
@ -33,7 +38,7 @@ const Language = ({isDesktop}:ILanguageProps) => {
}
</IntlContextConsumer>
</div>
);
) : null;
};
export default Language;

View file

@ -1,22 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the Language checks if component renders 1`] = `
<DocumentFragment>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</DocumentFragment>
`;
exports[`rendering of the Language checks if component renders 1`] = `<DocumentFragment />`;

View file

@ -0,0 +1,20 @@
@use '../../styles/design-system.scss' as *;
.lowIncomeContainer {
border: 1px solid #DFE1E2;
@include u-margin-top(4);
@include u-padding-left(4);
@include u-padding-right(3);
@include u-padding-bottom(4);
.lowIncomeTitle {
@include typeset('sans', 'xs', 3);
@include u-text('semibold');
}
.lowIncomeText {
@include typeset('sans', 'xs', 3);
@include u-text('light');
}
};

View file

@ -0,0 +1,14 @@
declare namespace LowIncomeNamespace {
export interface ILowIncomeScss {
lowIncomeContainer: string;
lowIncomeTitle: string;
lowIncomeText: string;
}
}
declare const LowIncomeScssModule: LowIncomeNamespace.ILowIncomeScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: LowIncomeNamespace.ILowIncomeScss;
};
export = LowIncomeScssModule;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import LowIncome from './LowIncome';
describe('rendering of the LowIncome', () => {
const {asFragment} = render(
<LocalizedComponent>
<LowIncome />
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,26 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
import * as styles from './LowIncome.module.scss';
const LowIncome = () => {
const intl = useIntl();
return (
<div className={styles.lowIncomeContainer}>
<p className={styles.lowIncomeTitle}>
<sup>*</sup>
{' '}
{intl.formatMessage(METHODOLOGY_COPY.LOW_INCOME.HEADING)}
</p>
<p className={styles.lowIncomeText}>
{intl.formatMessage(METHODOLOGY_COPY.LOW_INCOME.INFO)}
</p>
</div>
);
};
export default LowIncome;

View file

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the LowIncome checks if component renders 1`] = `
<DocumentFragment>
<div>
<p>
<sup>
*
</sup>
Low Income
</p>
<p>
At or above 65th percentile for percent of census tract population of households where household
income is at or below 200% of the federal poverty level
</p>
</div>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import LowIncome from './LowIncome';
export default LowIncome;

View file

@ -3,13 +3,14 @@ import {useIntl} from 'gatsby-plugin-intl';
import * as styles from './mapLegend.module.scss';
import * as EXPLORE_COPY from '../../data/copy/explore';
import DisadvantageDot from '../DisadvantageDot';
const MapLegend = () => {
const intl = useIntl();
return (
<div className={styles.legendContainer}>
<div className={styles.colorSwatch} />
<div>
<DisadvantageDot isBig={true} />
<div className={styles.legendTextBox}>
<div className={'j40-h4'}>
{intl.formatMessage(EXPLORE_COPY.LEGEND.PRIORITY_LABEL)}
</div>

View file

@ -1,26 +1,17 @@
@import "../utils.scss";
$min-color: #fafaf8;
$med-color: rgba(26, 68, 128, 0.2);
$max-color: rgba(26, 68, 128, 0.6);
$alertInfoColor: #e7f6f8;
.legendContainer {
display: flex;
margin: 1rem 0 1rem 2.5rem;
border: 1px solid #DFE1E2;
padding: .5rem 1.5rem 1rem 1.5rem;
margin: 1rem 0 1rem 2.5rem;
padding: 0 1.5rem 1rem 1.5rem;
.legendTextBox {
// Set maximum width for text area so that blue dot
// doesn't lose aspect ratio
flex-basis: 88%;
}
@media screen and (max-width: 640px) {
margin: 1rem 0 0;
}
}
.colorSwatch {
flex: 1 0 2rem;
box-sizing: border-box;
height: 2rem;
margin: 2rem 1.5rem 2rem 0;
border-radius: 50%;
background-color: $max-color;
}

View file

@ -1,7 +1,7 @@
declare namespace HowYouCanHelpModuleScssNamespace {
export interface IHowYouCanHelpModuleScss {
legendContainer: string;
colorSwatch: string;
legendTextBox: string;
}
}

View file

@ -8,12 +8,17 @@ exports[`rendering of the MapLegend checks if snapshots have changed 1`] = `
<div
class="j40-h4"
>
Draft community of focus
Disadvantaged community
</div>
<p
class="secondary"
>
A community identified as experiencing disadvantages that merits the focus of certain Federal investments, including through the Justice40 Initiative
Communities identified for the purposes of Justice40 as disadvantaged have been
historically marginalized, underserved, and overburdened by pollution. These communities
meet or exceed the criteria in one or more areas of focus.
</p>
</div>
</div>

View file

@ -22,9 +22,14 @@ const MapWrapper = ({location}: IMapWrapperProps) => {
<div className={styles.mapCaptionTextLink}>
{EXPLORE_COPY.DOWNLOAD_DRAFT.PARAGRAPH_1}
</div>
<div>
{EXPLORE_COPY.DOWNLOAD_DRAFT.PARAGRAPH_2}
</div>
</Grid>
</Grid>
<Grid row>
<Grid col={7}>
<h2>{EXPLORE_COPY.NOTE_ON_TERRITORIES.INTRO}</h2>
<p>{EXPLORE_COPY.NOTE_ON_TERRITORIES.PARA_1}</p>
<p>{EXPLORE_COPY.NOTE_ON_TERRITORIES.PARA_2}</p>
</Grid>
</Grid>
</>

View file

@ -0,0 +1,12 @@
@use '../../styles/design-system.scss' as *;
.formulaContainer {
@include u-margin-top(5);
p:not(:first-child) {
font-style: italic;
span {
@include u-text('bold');
}
}
};

View file

@ -0,0 +1,12 @@
declare namespace MethodologyFormulaNamespace {
export interface IMethodologyFormulaScss {
formulaContainer: string;
}
}
declare const MethodologyFormulaScssModule: MethodologyFormulaNamespace.IMethodologyFormulaScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: MethodologyFormulaNamespace.IMethodologyFormulaScss;
};
export = MethodologyFormulaScssModule;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import MethodologyFormula from './MethodologyFormula';
describe('rendering of the MethodologyFormula', () => {
const {asFragment} = render(
<LocalizedComponent>
<MethodologyFormula />
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,35 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
import * as styles from './MethodologyFormula.module.scss';
// The site shows the formula used in the methodology. The constants seen
// below aim to capture the 3 part of that formula. These are not
// reserved words.
const MethodologyFormula = () => {
const intl = useIntl();
return (
<section className={styles.formulaContainer}>
<p>
{intl.formatMessage(METHODOLOGY_COPY.PAGE.FORMULA_INTRO)}
</p>
<p>
{METHODOLOGY_COPY.FORMULA.IF}
</p>
<p>
{METHODOLOGY_COPY.FORMULA.AND}
</p>
<p>
{METHODOLOGY_COPY.FORMULA.THEN}
</p>
</section>
);
};
export default MethodologyFormula;

View file

@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the MethodologyFormula checks if component renders 1`] = `
<DocumentFragment>
<section>
<p>
Under the current formula, a census tract will be considered disadvantaged:
</p>
<p>
<span>
IF
</span>
it is above the threshold for one or more climate or environmental indicator
</p>
<p>
<span>
AND
</span>
it is above the threshold for one or more socioeconomic indicator
</p>
<p>
<span>
THEN
</span>
the community is considered disadvantaged.
</p>
</section>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import MethodologyFormula from './MethodologyFormula';
export default MethodologyFormula;

View file

@ -4,7 +4,7 @@
position: relative;
.surveyButton {
position: absolute;
position: fixed;
bottom: 0;
right: 2.2rem;

View file

@ -9,7 +9,7 @@ exports[`simulate app starting up, no click on map should match the snapshot of
>
<aside>
<header>
Zoom and select a census block group to view data
Zoom and select a census tract to view data
</header>
<div>
<img
@ -21,7 +21,13 @@ exports[`simulate app starting up, no click on map should match the snapshot of
Did you know?
</div>
<cite>
A census block group is generally between 600 and 3,000 people. It is the smallest geographical unit for which the U.S. Census Bureau publishes sample data.
A census tract is generally between 1,200 and 8,000 people, with an optimum size of 4,000 people.
Census tracts are small, relatively permanent subdivisions of a county defined by the
U.S. Census Bureau and usually cover a contiguous area. The census tract level represents the
smallest geographical unity that can be presented in a statistically sound manner, given the
datasets that are being used.
</cite>
</div>
</div>

View file

@ -1,125 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the component should match the snapshot of the MapIntroduction component 1`] = `
<DocumentFragment>
<h2>
Methodology
</h2>
<div
class="grid-row"
data-testid="grid"
>
<div
class="grid-col-7"
data-testid="grid"
>
<p>
The methodology for identifying communities of focus is calculated at the census block group level. Census block geographical boundaries are determined by the U.S. Census Bureau once every ten years. This tool utilizes the census block boundaries from 2010.
</p>
<p>
The following describes the process for identifying communities of focus.
</p>
</div>
</div>
<ol
class="usa-process-list"
>
<li
class="usa-process-list__item"
>
<h3
class="usa-process-list__heading"
data-testid="processListHeading"
>
Gather datasets
</h3>
<p>
</p>
<p
class="flush"
>
The methodology includes the following inputs that are equally weighted.
</p>
<h4>
Percent of Area Median Income
</h4>
<p
class="flush"
>
If a census block group is in a metropolitan area, this value is the median income of the census block group calculated as a percent of the metropolitan areas median income.
</p>
<p>
If a census block group is not in a metropolitan area, this value is the median income of the census block group calculated as a percent of the states median income.
</p>
<h4>
Percent of households below or at 100% of the federal poverty line
</h4>
This is the percent of households in a state with a household income
below or at 100% of the
<a
href="https://www.census.gov/topics/income-poverty/poverty/guidance/poverty-measures.html"
rel="noreferrer"
target="_blank"
>
federal poverty line
</a>
. This federal poverty line is calculated
based on the composition of each household (e.g., based on household size), but it does not vary geographically.
<h4>
The high school degree achievement rate for adults 25 years and older
</h4>
<p
class="flush"
>
The percent of individuals who are 25 or older who have received a high school degree.
</p>
</li>
<li
class="usa-process-list__item"
>
<h3
class="usa-process-list__heading"
data-testid="processListHeading"
>
Determine communites of focus
</h3>
<p>
</p>
<p
class="flush"
>
Under the existing formula, a census block group will be considered a community of focus if:
</p>
<p>
(The median income is less than 80% of the area median income
</p>
<p
class="flush"
>
OR
</p>
<p
class="flush"
>
households living in poverty (at or below 100% of the federal poverty level) is greater than 20%)
</p>
<p
class="flush"
>
AND
</p>
<p
class="flush"
>
The high school degree achievement rate for adults 25 years and older is greater than 95%
</p>
</li>
</ol>
</DocumentFragment>
`;

View file

@ -11,6 +11,6 @@ describe('rendering of the component', () => {
);
it('renders the title', () => {
expect(screen.getByRole('banner')).toHaveTextContent('Zoom and select a census block group to view data');
expect(screen.getByRole('banner')).toHaveTextContent('Zoom and select a census tract to view data');
});
});

View file

@ -1,28 +0,0 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import ScoreStepsList from './scoreStepsList';
import {LocalizedComponent} from '../test/testHelpers';
// TODO: Move this to a location that will detect on all tests
// See ticket: #550
beforeAll(() => {
jest.spyOn(global.console, 'error').mockImplementation((...params) => {
console.error(params);
});
});
describe('rendering of the component', () => {
const {asFragment} = render(
<LocalizedComponent>
<ScoreStepsList/>
</LocalizedComponent>,
);
it('should match the snapshot of the MapIntroduction component', () => {
expect(asFragment()).toMatchSnapshot();
});
it('No console errors', () => {
expect(console.error).toBeCalledTimes(0);
});
});

View file

@ -66,10 +66,10 @@ const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
short: intl.formatMessage(EXPLORE_COPY.MAP.PR_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.PR_LONG),
},
{
short: intl.formatMessage(EXPLORE_COPY.MAP.GU_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.GU_LONG),
},
// {
// short: intl.formatMessage(EXPLORE_COPY.MAP.GU_SHORT),
// long: intl.formatMessage(EXPLORE_COPY.MAP.GU_LONG),
// },
{
short: intl.formatMessage(EXPLORE_COPY.MAP.AS_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.AS_LONG),
@ -78,10 +78,10 @@ const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
short: intl.formatMessage(EXPLORE_COPY.MAP.MP_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.MP_LONG),
},
{
short: intl.formatMessage(EXPLORE_COPY.MAP.VI_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.VI_LONG),
},
// {
// short: intl.formatMessage(EXPLORE_COPY.MAP.VI_SHORT),
// long: intl.formatMessage(EXPLORE_COPY.MAP.VI_LONG),
// },
];
// the offset for this array should map the territories variable
const territoriesIconClassName = [
@ -89,10 +89,10 @@ const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
'mapboxgl-ctrl-zoom-to-ak',
'mapboxgl-ctrl-zoom-to-hi',
'mapboxgl-ctrl-zoom-to-pr',
'mapboxgl-ctrl-zoom-to-gu',
// 'mapboxgl-ctrl-zoom-to-gu',
'mapboxgl-ctrl-zoom-to-as',
'mapboxgl-ctrl-zoom-to-mp',
'mapboxgl-ctrl-zoom-to-vi',
// 'mapboxgl-ctrl-zoom-to-vi',
];
return (

View file

@ -30,7 +30,7 @@ export const FEATURE_TILE_LOW_ZOOM_URL = featureURLForTilesetName('low');
export const PERFORMANCE_MARKER_MAP_IDLE = 'MAP_IDLE';
// Properties
export const SCORE_PROPERTY_HIGH = 'Definition L (percentile)';
export const SCORE_PROPERTY_HIGH = 'SL_PFS';
export const SCORE_PROPERTY_LOW = 'L_SCORE';
export const GEOID_PROPERTY = 'GEOID10';
export const HIGH_SCORE_SOURCE_NAME = 'score-high';
@ -42,28 +42,73 @@ export const CURRENTLY_SELECTED_FEATURE_HIGHLIGHT_LAYER_NAME = 'currently-select
export const BLOCK_GROUP_BOUNDARY_LAYER_NAME = 'block-group-boundary-layer';
// Properties
export const POVERTY_PROPERTY_PERCENTILE = 'Poverty (Less than 200% of federal poverty line) (percentile)';
export const HOUSING_BURDEN_PROPERTY_PERCENTILE = 'Housing burden (percent) (percentile)';
export const LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE = 'Linguistic isolation (percent) (percentile)';
export const UNEMPLOYMENT_PROPERTY_PERCENTILE = 'Unemployed civilians (percent) (percentile)';
export const TOTAL_POPULATION = 'Total population';
export const EDUCATION_PROPERTY_PERCENTILE =
`Percent individuals age 25 or over with less than high school degree (percentile)`;
export const COUNTY_NAME = 'County Name';
export const STATE_NAME = 'State Name';
export const DIABETES_PERCENTILE = 'Diagnosed diabetes among adults aged >=18 years (percentile)';
export const ASTHMA_PERCENTILE = 'Current asthma among adults aged >=18 years (percentile)';
export const HEART_PERCENTILE = 'Coronary heart disease among adults aged >=18 years (percentile)';
export const LIFE_PERCENTILE = 'Life expectancy (years) (percentile)';
export const TRAFFIC_PERCENTILE = 'Traffic proximity and volume (percentile)';
export const FEMA_PERCENTILE = 'FEMA Risk Index Expected Annual Loss Score (percentile)';
export const ENERGY_PERCENTILE = 'Energy burden (percentile)';
export const WASTEWATER_PERCENTILE = 'Wastewater discharge (percentile)';
export const LEAD_PAINT_PERCENTILE = 'Percent pre-1960s housing (lead paint indicator) (percentile)';
export const DIESEL_MATTER_PERCENTILE = 'Diesel particulate matter (percentile)';
export const PM25_PERCENTILE = 'Particulate matter (PM2.5) (percentile)';
export const AREA_MEDIAN_INCOME_PERCENTILE = 'Median household income (% of AMI) (percentile)';
// Indicator values:
export const ASTHMA_PERCENTILE = 'AF_PFS';
export const COUNTY_NAME = 'CF';
export const DIABETES_PERCENTILE = 'DF_PFS';
export const DIESEL_MATTER_PERCENTILE = 'DSF_PFS';
export const ENERGY_PERCENTILE = 'EBF_PFS';
export const HEART_PERCENTILE = 'HDF_PFS';
export const HIGH_SCHOOL_PROPERTY_PERCENTILE = `HSEF`;
export const HOUSING_BURDEN_PROPERTY_PERCENTILE = 'HBF_PFS';
export const LEAD_PAINT_PERCENTILE = 'LPF_PFS';
export const LIFE_PERCENTILE = 'LLEF_PFS';
export const LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE = 'LIF_PFS';
export const LOW_MEDIAN_INCOME_PERCENTILE = 'LMI_PFS';
export const PM25_PERCENTILE = 'PM25F_PFS';
export const POVERTY_PROPERTY_PERCENTILE = 'P200_PFS';
export const STATE_NAME = 'SF';
export const TOTAL_POPULATION = 'TPF';
export const TRAFFIC_PERCENTILE = 'TF_PFS';
export const UNEMPLOYMENT_PROPERTY_PERCENTILE = 'UF_PFS';
export const WASTEWATER_PERCENTILE = 'WF_PFS';
export const EXP_AGRICULTURE_LOSS_PERCENTILE = 'EALR_PFS';
export const EXP_BUILDING_LOSS_PERCENTILE = 'EBLR_PFS';
export const EXP_POPULATION_LOSS_PERCENTILE = 'EPLR_PFS';
export const MEDIAN_HOME_VALUE_PERCENTILE = 'MHVF_PFS';
export const POVERTY_BELOW_100_PERCENTILE = 'P100_PFS';
export const POVERTY_BELOW_200_PERCENTILE = 'P200_PFS';
export const PROXIMITY_NPL_SITES_PERCENTILE = 'NPL_PFS';
export const PROXIMITY_RMP_SITES_PERCENTILE = 'RMP_PFS';
export const PROXIMITY_TSDF_SITES_PERCENTILE = 'TSDF_PFS';
// Category booleans (disadvantaged or not):
export const IS_CLIMATE_FACTOR_DISADVANTAGED_L = 'L_CLT';
export const IS_WORKFORCE_FACTOR_DISADVANTAGED_L = 'L_WKFC';
export const IS_WATER_FACTOR_DISADVANTAGED_L = 'L_WTR';
export const IS_ENERGY_FACTOR_DISADVANTAGED_L = 'L_ENY';
export const IS_TRANSPORT_FACTOR_DISADVANTAGED_L = 'L_TRN';
export const IS_HOUSING_FACTOR_DISADVANTAGED_L = 'L_HSG';
export const IS_POLLUTION_FACTOR_DISADVANTAGED_L = 'L_PLN';
export const IS_HEALTH_FACTOR_DISADVANTAGED_L = 'L_HLTH';
// Indicator booleans (disadvangted or not): (GTE = greater than or equal)
export const IS_GTE_90_EXP_POP_LOSS_AND_IS_LOW_INCOME = 'EPLRLI';
export const IS_GTE_90_EXP_AGR_LOSS_AND_IS_LOW_INCOME = 'EALRLI';
export const IS_GTE_90_EXP_BLD_LOSS_AND_IS_LOW_INCOME = 'EBLRLI';
export const IS_GTE_90_PM25_AND_IS_LOW_INCOME = 'PM25LI';
export const IS_GTE_90_ENERGY_BURDEN_AND_IS_LOW_INCOME = 'EBLI';
export const IS_GTE_90_DIESEL_PM_AND_IS_LOW_INCOME = 'DPMLI';
export const IS_GTE_90_TRAFFIC_PROX_AND_IS_LOW_INCOME = 'TPLI';
export const IS_GTE_90_LEAD_PAINT_AND_MEDIAN_HOME_VAL_AND_IS_LOW_INCOME = 'LPMHVLI';
export const IS_GTE_90_HOUSE_BURDEN_AND_IS_LOW_INCOME = 'HBLI';
export const IS_GTE_90_RMP_AND_IS_LOW_INCOME = 'RMPLI';
export const IS_GTE_90_SUPERFUND_AND_IS_LOW_INCOME = 'SFLI';
export const IS_GTE_90_HAZARD_WASTE_AND_IS_LOW_INCOME = 'HWLI';
export const IS_GTE_90_WASTEWATER_AND_IS_LOW_INCOME = 'WDLI';
export const IS_GTE_90_DIABETES_AND_IS_LOW_INCOME = 'DLI';
export const IS_GTE_90_ASTHMA_AND_IS_LOW_INCOME = 'ALI';
export const IS_GTE_90_HEART_DISEASE_AND_IS_LOW_INCOME = 'HDLI';
export const IS_GTE_90_LOW_LIFE_EXP_AND_IS_LOW_INCOME = 'LLELI';
export const IS_GTE_90_LINGUISITIC_ISO_AND_IS_LOW_INCOME = 'LILHSE';
export const IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU = 'PLHSE';
export const IS_GTE_90_LOW_MEDIAN_INCOME_AND_LOW_HIGH_SCHOOL_EDU = 'LMILHSE';
export const IS_GTE_90_UNEMPLOYMENT_AND_LOW_HIGH_SCHOOL_EDU = 'ULHSE';
export const IS_FEDERAL_POVERTY_LEVEL_200 = 'FPL200S';
export const TOTAL_THRESHOLD_CRITERIA = 'TC';
export const IS_GTE_90_ISLAND_AREA_UNEMPLOYMENT_AND_IS_LOW_HS_EDU_2009 = 'IAULHSE';
export const IS_GTE_90_ISLAND_AREA_BELOW_100_POVERTY_AND_IS_LOW_HS_EDU_2009 = 'ISPLHSE';
export const IS_GTE_90_ISLAND_AREA_LOW_MEDIAN_INCOME_AND_IS_LOW_HS_EDU_2009 = 'IALMILHSE';
export type J40Properties = { [key: string]: any };
@ -136,7 +181,7 @@ export const DEFAULT_OUTLINE_COLOR = '#4EA5CF';
export const MIN_COLOR = '#FFFFFF';
export const MED_COLOR = '#D1DAE6';
export const MAX_COLOR = '#768FB3';
export const BORDER_HIGHLIGHT_COLOR = '#00BDE3';
export const BORDER_HIGHLIGHT_COLOR = '#1A4480';
export const CURRENTLY_SELECTED_FEATURE_LAYER_OPACITY = 0.5;
// Widths

View file

@ -1,4 +1,6 @@
import React from 'react';
import {defineMessages} from 'react-intl';
import {FormattedMessage, Link} from 'gatsby-plugin-intl';
export const PAGE = defineMessages({
TITLE: {
@ -11,24 +13,22 @@ export const PAGE = defineMessages({
defaultMessage: 'Page not found',
description: 'page not found heading text',
},
APOLOGY: {
id: 'pageNotFound.apology.text',
defaultMessage: 'Sorry',
description: 'page not found apology text',
},
APOLOGY_INFO: {
id: 'pageNotFound.apology.description.text',
defaultMessage: 'we couldnt find what you were looking for.',
description: 'page not found apology description text',
},
GUIDANCE: {
id: 'pageNotFound.Guidance.text',
defaultMessage: 'Try creating a page in',
description: 'page not found guidance text',
},
LINK_HOME: {
id: 'pageNotFound.link.to.go.home.text',
defaultMessage: 'Go home',
description: 'page not found link to go home text',
},
});
export const ERROR_MSG =
<FormattedMessage
id={'pageNotFound.apology.text'}
defaultMessage={`
Sorry, the page you were looking for was not found. Click {home} to go home.
`}
description={'page description'}
values={{
home: <Link to={'/'}>here</Link>,
homeEs: <Link to={'/methodology'}>aqui</Link>,
}}
/>;

View file

@ -1,4 +1,8 @@
import React from 'react';
import {defineMessages} from 'react-intl';
import {FormattedMessage} from 'gatsby-plugin-intl';
export const EXEC_ORDER_LINK = 'https://www.federalregister.gov/documents/2021/02/01/2021-02177/tackling-the-climate-crisis-at-home-and-abroad#:~:text=Sec.%20223.%20Justice40,40-percent%20goal.';
export const PAGE = defineMessages({
TILE: {
@ -13,12 +17,12 @@ export const PAGE = defineMessages({
},
HEADING_1: {
id: 'index.heading.screentool',
defaultMessage: 'The screening tool',
defaultMessage: 'Screening tool',
description: 'heading for about screening tool',
},
HEADING1_DESCRIPTION1: {
id: 'about.page.sub.header.1.text.1',
defaultMessage: 'On January 27, 2021, President Biden directed the Council on'+
defaultMessage: 'In Executive Order 14008 on Tackling the Climate Crisis at Home and Abroad'+
' Environmental Quality (CEQ) to create a climate and economic'+
' justice screening tool. The purpose of the tool is to help'+
' Federal agencies identify disadvantaged communities and provide'+
@ -32,12 +36,12 @@ export const PAGE = defineMessages({
HEADING1_DESCRIPTION2: {
id: 'about.page.sub.header.1.text.2',
defaultMessage: 'The current version of the tool is in a public beta form and'+
' will be updated based on feedback from the public.',
' will be updated based on feedback and research.',
description: 'about page sub header text',
},
HEADING_2: {
id: 'index.heading.justice40',
defaultMessage: 'The Justice40 Initiative',
defaultMessage: 'Justice40 Initiative',
description: 'heading for about justice 40',
},
HEADING2_DESCRIPTION1: {
@ -53,20 +57,54 @@ export const PAGE = defineMessages({
' critical clean water infrastructure.',
description: 'about page sub header text',
},
HEADING2_DESCRIPTION2: {
id: 'about.page.sub.header.2.text.2',
defaultMessage: 'Read more about the Justice40 Initiative in President Bidens',
description: 'about page sub header text',
},
PRESIDENT_LINK_LABEL: {
id: 'index.presidentalLinkLabel',
defaultMessage: 'Executive Order 14008 on Tackling the Climate Crisis at Home and Abroad.',
description: 'Link url to presidential actions executive order. Part of paragraph 2',
},
});
export const EXEC_ORDER_LINK = 'https://www.whitehouse.gov/briefing-room/presidential-actions/2021/01/27/' +
'executive-order-on-tackling-the-climate-crisis-at-home-and-abroad/';
export const HEADING_1 = {
DESCRIPTION_1:
<FormattedMessage
id={'about.page.sub.header.1.text.1'}
description={'about page sub header text'}
defaultMessage={`
In {eoLink} on Tackling the Climate Crisis at Home and Abroad,
President Biden directed the Council on
Environmental Quality (CEQ) to create a Climate and Economic
Justice Screening Tool. The purpose of the tool is to help
Federal agencies identify disadvantaged communities
that have been historically marginalized, underserved, and
overburdened by pollution. The tool provides
socioeconomic, environmental, and climate information
to inform decisions that may affect these communities. The tool
identifies disadvantaged communities
through publicly available, nationally consistent, high-quality
data.
`}
values={{
eoLink:
<a href={EXEC_ORDER_LINK}>
Executive Order 14008
</a>,
}}
/>,
};
export const HEADING_2 = {
DESCRIPTION_2:
<FormattedMessage
id={'about.page.sub.header.2.text.2'}
description={'about page sub header text'}
defaultMessage={`
Read more about the Justice40 Initiative in President Bidens
{eoLink} on Tackling the Climate Crisis at Home and Abroad.
`}
values={{
eoLink:
<a href={EXEC_ORDER_LINK}>
Executive Order 14008
</a>,
}}
/>,
};
export const GITHUB_LINK = 'https://github.com/usds/justice40-tool';
@ -83,14 +121,15 @@ export const HOW_TO_GET_STARTED = defineMessages({
},
FEDERAL_PM_INFO: {
id: 'federal.pm.info',
defaultMessage: 'Download the screening tools draft list of communities of focus.'+
' Explore data that may be useful to your program, and provide'+
' feedback on the tool.',
defaultMessage: `
Download the tools current list of communities, explore data that may be useful to your
program, and provide feedback on the tool.
`,
description: 'sub heading of page',
},
FEDERAL_PM_LINK_TEXT: {
id: 'federal.pm.link',
defaultMessage: 'Go to data & methodology',
defaultMessage: 'Methodology & data',
description: 'link text to go to methodology page',
},
COMMUNITY_MEMBERS_HEADING: {
@ -100,8 +139,9 @@ export const HOW_TO_GET_STARTED = defineMessages({
},
COMMUNITY_MEMBERS_INFO: {
id: 'community.members.info',
defaultMessage: 'Explore data about communities of focus in your area, and help '+
' provide feedback on the tool.',
defaultMessage: `
Explore data about communities in your area and provide feedback on the tool.
`,
description: 'sub heading of page',
},
COMMUNITY_MEMBERS_LINK_TEXT: {

View file

@ -39,7 +39,7 @@ export const HEADER = defineMessages({
},
METHODOLOGY: {
id: 'header.methodology',
defaultMessage: 'Data & methodology',
defaultMessage: 'Methodology & data',
description: 'Header navigate item to the Methodology page',
},
CONTACT: {

View file

@ -21,30 +21,34 @@ export const PAGE_INTRO = defineMessages({
export const PAGE_DESCRIPTION = <FormattedMessage
id={'exploreTool.page.description'}
defaultMessage={
`Zoom into the map to see communities of focus that can help Federal agencies
identify disadvantaged communities and to provide socioeconomic,
environmental, and climate information and data. Learn more about the methodology
and datasets that were used to determine these communities of focus on the
{methodologyLink}
page.`}
defaultMessage={`
Use the map to see disadvantaged communities that have been historically
marginalized, underserved, and overburdened by pollution. The map uses
publicly-available, nationally-consistent, high-quality datasets. Learn more about
the methodology and datasets that were used to identify disavantaged communities
on the {methodologyLink} page.
`}
description={'page description'}
values={{
methodologyLink: <Link to={'/methodology'}>Data & methodology</Link>,
methodologyLinkEs: <Link to={'/methodology'}>Datos y metodología</Link>,
methodologyLink: <Link to={'/methodology'}>methodology & data</Link>,
methodologyLinkEs: <Link to={'/methodology'}>metodología y datos</Link>,
}}
/>;
export const LEGEND = defineMessages({
PRIORITY_LABEL: {
id: 'legend.info.priority.label',
defaultMessage: 'Draft community of focus',
defaultMessage: 'Disadvantaged community',
description: 'the label of the prioritized community legend',
},
PRIORITY_DESCRIPT: {
id: 'legend.info.threshold.label',
defaultMessage: 'A community identified as experiencing disadvantages that merits' +
' the focus of certain Federal investments, including through the Justice40 Initiative',
defaultMessage: `
Communities identified for the purposes of Justice40 as disadvantaged have been
historically marginalized, underserved, and overburdened by pollution. These communities
meet or exceed the criteria in one or more areas of focus.
`,
description: 'the label of the threshold community legend',
},
});
@ -149,11 +153,12 @@ export const MAP = defineMessages({
},
});
// Side Panel copy
export const SIDE_PANEL_INITIAL_STATE = defineMessages({
TITLE: {
id: 'mapIntro.mapIntroHeader',
defaultMessage: 'Zoom and select a census block group to view data',
defaultMessage: 'Zoom and select a census tract to view data',
description: 'introductory text of ways to use the map',
},
DID_YOU_KNOW: {
@ -163,18 +168,33 @@ export const SIDE_PANEL_INITIAL_STATE = defineMessages({
},
CBG_DEFINITION: {
id: 'mapIntro.censusBlockGroupDefinition',
defaultMessage: 'A census block group is generally between 600 and 3,000 people. ' +
'It is the smallest geographical unit for which the U.S. Census ' +
'Bureau publishes sample data.',
defaultMessage: `
A census tract is generally between 1,200 and 8,000 people, with an optimum size of 4,000 people.
Census tracts are small, relatively permanent subdivisions of a county defined by the
U.S. Census Bureau and usually cover a contiguous area. The census tract level represents the
smallest geographical unity that can be presented in a statistically sound manner, given the
datasets that are being used.
`,
description: 'cites the definition and helpful information about census groups',
},
});
export const SIDE_PANEL_VERION = {
TITLE: <FormattedMessage
id={'areaDetail.side.panel.version.title'}
defaultMessage={ 'Methodology version {version}'}
description={'methodology version number'}
values= {{
version: METHODOLOGY_COPY.VERSION_NUMBER,
}}
/>,
};
export const SIDE_PANEL_CBG_INFO = defineMessages({
CENSUS_BLOCK_GROUP: {
id: 'areaDetail.geographicInfo.censusBlockGroup',
defaultMessage: 'Census block group:',
description: 'the census block group id number of the feature selected',
defaultMessage: 'Census tract:',
description: 'the census tract id number of the feature selected',
},
COUNTY: {
id: 'areaDetail.geographicInfo.county',
@ -196,41 +216,148 @@ export const SIDE_PANEL_CBG_INFO = defineMessages({
export const COMMUNITY = {
OF_FOCUS: <FormattedMessage
id={'areaDetail.categorization.community.of.focus'}
defaultMessage={ 'Community of focus'}
defaultMessage={ 'YES'}
description={'the communities the score currently is focused on'}
/>,
NOT_OF_FOCUS: <FormattedMessage
id= {'areaDetail.categorization.not.community.of.focus'}
defaultMessage= {'Not a community of focus'}
defaultMessage= {'No'}
description= {'the communities the score currently is not focused on'}
/>,
IS_FOCUS: <FormattedMessage
id={'areaDetail.categorization.is.community.of.focus'}
defaultMessage={ 'Identified as disadvantaged?'}
description={'asking IF the communities is focused on'}
/>,
SEND_FEEDBACK: <FormattedMessage
id={'areaDetail.categorization.send.feedback'}
defaultMessage={ 'send feedback'}
description={'link to send feedback'}
/>,
};
export const SIDE_PANEL_CATEGORY = defineMessages({
CLIMATE: {
id: 'areaDetail.indicator.title.climate',
defaultMessage: 'Climate change',
description: 'Climate change title',
},
CLEAN_ENERGY: {
id: 'areaDetail.indicator.title.clean.energy',
defaultMessage: 'Clean, efficient energy ',
description: 'Clean, efficient energy title',
},
CLEAN_TRANSPORT: {
id: 'areaDetail.indicator.title.clean.transport',
defaultMessage: 'Clean transportation',
description: 'Clean transportation title',
},
SUSTAIN_HOUSE: {
id: 'areaDetail.indicator.title.sustain.house',
defaultMessage: 'Sustainable housing',
description: 'Sustainable housing title',
},
LEG_POLLUTE: {
id: 'areaDetail.indicator.title.legacy.pollution',
defaultMessage: 'Legacy pollution',
description: 'Legacy pollution title',
},
CLEAN_WATER: {
id: 'areaDetail.indicator.title.clean.water',
defaultMessage: 'Clean water and waste',
description: 'Clean water and waste title',
},
HEALTH_BURDEN: {
id: 'areaDetail.indicator.title.health.burden',
defaultMessage: 'Health burdens',
description: 'Health burdens title',
},
WORK_DEV: {
id: 'areaDetail.indicator.title.work.dev',
defaultMessage: 'Workforce development',
description: 'Workforce development title',
},
});
export const SIDE_PANEL_INDICATORS = defineMessages({
INDICATOR_COLUMN_HEADER: {
id: 'areaDetail.indicators.indicatorColumnHeader',
defaultMessage: 'Indicator',
description: 'the population of the feature selected',
EXP_AG_LOSS: {
id: 'areaDetail.indicator.exp.ag.loss',
defaultMessage: 'Expected agriculture loss rate',
description: 'agriculture loss rate',
},
PERCENTILE_COLUMN_HEADER: {
id: 'areaDetail.indicators.percentileColumnHeader',
defaultMessage: 'Percentile (0-100)',
description: 'the population of the feature selected',
EXP_BLD_LOSS: {
id: 'areaDetail.indicator.exp.bld.loss',
defaultMessage: 'Expected building loss rate',
description: 'building loss rate',
},
POVERTY: {
id: 'areaDetail.indicator.poverty',
defaultMessage: 'Poverty',
description: 'Household income is less than or equal to twice the federal "poverty level"',
EXP_POP_LOSS: {
id: 'areaDetail.indicator.exp.pop.loss',
defaultMessage: 'Expected population loss rate',
description: 'population loss rate',
},
AREA_MEDIAN_INCOME: {
id: 'areaDetail.indicator.areaMedianIncome',
defaultMessage: 'Area Median Income',
description: 'calculated as percent of the area median income',
LOW_INCOME: {
id: 'areaDetail.indicator.low.income',
defaultMessage: 'Low income',
description: 'low income',
},
EDUCATION: {
id: 'areaDetail.indicator.education',
defaultMessage: 'Education, less than high school',
description: 'Percent of people age 25 or older that didnt get a high school diploma',
ENERGY_BURDEN: {
id: 'areaDetail.indicator.energyBurden',
defaultMessage: 'Energy cost burden',
description: 'Average annual energy cost ($) divided by household income',
},
PM_2_5: {
id: 'areaDetail.indicator.pm25',
defaultMessage: 'PM2.5',
description: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
},
DIESEL_PARTICULATE_MATTER: {
id: 'areaDetail.indicator.dieselPartMatter',
defaultMessage: 'Diesel particulate matter',
description: 'Diesel particulate matter level in air',
},
TRAFFIC_VOLUME: {
id: 'areaDetail.indicator.trafficVolume',
defaultMessage: 'Traffic',
description: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
},
LEAD_PAINT: {
id: 'areaDetail.indicator.leadPaint',
defaultMessage: 'Lead paint',
description: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
},
MED_HOME_VAL: {
id: 'areaDetail.indicator.med.home.val',
defaultMessage: 'Median home value',
description: 'Median home value of owner-occupied housing units in the area.',
},
HOUSE_BURDEN: {
id: 'areaDetail.indicator.houseBurden',
defaultMessage: 'Housing cost burden',
description: 'People ages 18 and up who report having been told by a doctor,' +
' nurse, or other health professionals that they have diabetes other than diabetes during pregnancy',
},
PROX_HAZ: {
id: 'areaDetail.indicator.prox.haz',
defaultMessage: 'Proximity to hazardous waste facilities',
description: 'Count of hazardous waste facilities ',
},
PROX_NPL: {
id: 'areaDetail.indicator.prox.npl',
defaultMessage: 'Proximity to NPL sites',
description: 'Count of proposed or listed NPL ',
},
PROX_RMP: {
id: 'areaDetail.indicator.prox.rmp',
defaultMessage: 'Proximity to RMP sites',
description: 'Count of proposed or listed RMP',
},
WASTE_WATER: {
id: 'areaDetail.indicator.wasteWater',
defaultMessage: 'Wastewater discharge',
description: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
},
ASTHMA: {
id: 'areaDetail.indicator.asthma',
@ -242,180 +369,201 @@ export const SIDE_PANEL_INDICATORS = defineMessages({
defaultMessage: 'Diabetes',
description: 'diabetes from dr or nurse',
},
DIESEL_PARTICULATE_MATTER: {
id: 'areaDetail.indicator.dieselPartMatter',
defaultMessage: 'Diesel particulate matter',
description: 'Diesel particulate matter level in air',
},
ENERGY_BURDEN: {
id: 'areaDetail.indicator.energyBurden',
defaultMessage: 'Energy burden',
description: 'Average annual energy cost ($) divided by household income',
},
FEMA_RISK: {
id: 'areaDetail.indicator.femaRisk',
defaultMessage: 'FEMA Risk Index',
description: 'Risk based on 18 natural hazard types, in addition to a '+
'community\'s social vulnerability and community resilience',
},
HEART_DISEASE: {
id: 'areaDetail.indicator.heartDisease',
defaultMessage: 'Heart disease',
description: 'People ages 18 and up who report ever having been told by a' +
'doctor, nurse, or other health professionals that they had angina or coronary heart disease',
},
HOUSE_BURDEN: {
id: 'areaDetail.indicator.houseBurden',
defaultMessage: 'Housing cost burden',
description: 'People ages 18 and up who report having been told by a doctor,' +
' nurse, or other health professionals that they have diabetes other than diabetes during pregnancy',
},
LEAD_PAINT: {
id: 'areaDetail.indicator.leadPaint',
defaultMessage: 'Lead paint',
description: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
},
LIFE_EXPECT: {
id: 'areaDetail.indicator.lifeExpect',
defaultMessage: 'Life expectancy',
defaultMessage: 'Low life expectancy',
description: 'Estimated years of life expectancy',
},
PM_2_5: {
id: 'areaDetail.indicator.pm25',
defaultMessage: 'PM2.5',
description: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
LOW_MED_INC: {
id: 'areaDetail.indicator.low.med.income',
defaultMessage: 'Low median income',
description: 'Low median income',
},
TRAFFIC_VOLUME: {
id: 'areaDetail.indicator.trafficVolume',
defaultMessage: 'Traffic proximity and volume',
description: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
LING_ISO: {
id: 'areaDetail.indicator.ling.iso',
defaultMessage: 'Linguistic isolation',
description: 'Linguistic isolation',
},
WASTE_WATER: {
id: 'areaDetail.indicator.wasteWater',
defaultMessage: 'Wastewater discharge',
description: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
UNEMPLOY: {
id: 'areaDetail.indicator.unemploy',
defaultMessage: 'Unemployment',
description: 'Unemployment',
},
POVERTY: {
id: 'areaDetail.indicator.poverty',
defaultMessage: 'Poverty',
description: 'Unemployment',
},
HIGH_SCL: {
id: 'areaDetail.indicator.high.school',
defaultMessage: 'High school degree achievement rate',
description: 'High school degree achievement rate',
},
});
export const SIDE_PANEL_INDICATOR_DESCRIPTION = defineMessages({
AREA_MEDIAN_INCOME: {
id: 'areaDetail.indicator.description.area_median_income',
defaultMessage: 'Median income of the census block group calculated as a percent of the metropolitan'+
' areas or state\'s median income',
description: 'Median income of the census block group calculated as a percent of the metropolitan'+
' areas or state\'s median income',
EXP_AG_LOSS: {
id: 'areaDetail.indicator.description.exp.ag.loss',
defaultMessage: 'Economic loss rate to agriculture resulting from natural hazards',
description: 'Economic loss rate to agriculture resulting from natural hazards',
},
EDUCATION: {
id: 'areaDetail.indicator.description.education',
defaultMessage: 'Percent of people age 25 or older that didnt get a high school diploma',
description: 'Percent of people age 25 or older that didnt get a high school diploma',
EXP_BLD_LOSS: {
id: 'areaDetail.indicator.description.exp.bld.loss',
defaultMessage: 'Economic loss rate to buildings resulting from natural hazards',
description: 'Economic loss rate to buildings resulting from natural hazards',
},
POVERTY: {
id: 'areaDetail.indicator.description.poverty',
defaultMessage: 'Percent of a block group\'s population in households where the household income' +
' is at or below 100% of the federal poverty level',
description: 'Percent of a block group\'s population in households where the household income' +
' is at or below 100% of the federal poverty level',
EXP_POP_LOSS: {
id: 'areaDetail.indicator.description.exp.pop.loss',
defaultMessage: 'Economic loss rate to the population in fatalities and injuries resulting from natural hazards',
description: 'Economic loss rate to the population in fatalities and injuries resulting from natural hazards',
},
ASTHMA: {
id: 'areaDetail.indicator.description.asthma',
defaultMessage: 'People who answer “yes” to both of the questions: “Have you ever been told by' +
' a doctor nurse, or other health professional that you have asthma?” and “Do you still have asthma?"',
description: 'People who answer “yes” to both of the questions: “Have you ever been told by' +
' a doctor nurse, or other health professional that you have asthma?” and “Do you still have asthma?"',
},
DIABETES: {
id: 'areaDetail.indicator.description.diabetes',
defaultMessage: 'People ages 18 and up who report having been told by a doctor, nurse, or other' +
' health professionals that they have diabetes other than diabetes during pregnancy',
description: 'People ages 18 and up who report having been told by a doctor, nurse, or other' +
' health professionals that they have diabetes other than diabetes during pregnancy',
},
DIESEL_PARTICULATE_MATTER: {
id: 'areaDetail.indicator.description.dieselPartMatter',
defaultMessage: 'Mixture of particles that is part of diesel exhaust in the air',
description: 'Mixture of particles that is part of diesel exhaust in the air',
LOW_INCOME: {
id: 'areaDetail.indicator.description.low.income',
defaultMessage: 'Household income is less than or equal to twice the federal poverty level',
description: 'Household income is less than or equal to twice the federal poverty level',
},
ENERGY_BURDEN: {
id: 'areaDetail.indicator.description.energyBurden',
defaultMessage: 'Average annual energy cost ($) divided by household income',
description: 'Average annual energy cost ($) divided by household income',
},
FEMA_RISK: {
id: 'areaDetail.indicator.description.femaRisk',
defaultMessage: 'Expected Annual Loss Score, which is the average economic loss in dollars' +
' resulting from natural hazards each year.',
description: 'Expected Annual Loss Score, which is the average economic loss in dollars' +
' resulting from natural hazards each year.',
},
HEART_DISEASE: {
id: 'areaDetail.indicator.description.heartDisease',
defaultMessage: 'People ages 18 and up who report ever having been told by a' +
' doctor, nurse, or other health professionals that they had angina or coronary heart disease',
description: 'People ages 18 and up who report ever having been told by a' +
' doctor, nurse, or other health professionals that they had angina or coronary heart disease',
},
HOUSE_BURDEN: {
id: 'areaDetail.indicator.description.houseBurden',
defaultMessage: 'Households that are low income and spend more than 30% of their income on' +
' housing costs',
description: 'Households that are low income and spend more than 30% of their income on' +
' housing costs',
},
LEAD_PAINT: {
id: 'areaDetail.indicator.description.leadPaint',
defaultMessage: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
description: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
},
LIFE_EXPECT: {
id: 'areaDetail.indicator.description.lifeExpect',
defaultMessage: 'Estimated years of life expectancy',
description: 'Estimated years of life expectancy',
defaultMessage: 'Energy costs divided by household income',
description: 'Energy costs divided by household income',
},
PM_2_5: {
id: 'areaDetail.indicator.description.pm25',
defaultMessage: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
description: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
defaultMessage: 'Fine inhalable particles, 2.5 micrometers and smaller',
description: 'Fine inhalable particles, 2.5 micrometers and smaller',
},
DIESEL_PARTICULATE_MATTER: {
id: 'areaDetail.indicator.description.dieselPartMatter',
defaultMessage: 'Diesel exhaust in the air',
description: 'Diesel exhaust in the air',
},
TRAFFIC_VOLUME: {
id: 'areaDetail.indicator.description.trafficVolume',
defaultMessage: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
description: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
defaultMessage: 'Count of vehicles at major roads within 500 meters',
description: 'Count of vehicles at major roads within 500 meters',
},
LEAD_PAINT: {
id: 'areaDetail.indicator.description.leadPaint',
defaultMessage: 'Pre-1960 housing',
description: 'Pre-1960 housing',
},
MED_HOME_VAL: {
id: 'areaDetail.indicator.description.med.home.val',
defaultMessage: 'Meidan home value in area',
description: 'Meidan home value in area',
},
HOUSE_BURDEN: {
id: 'areaDetail.indicator.description.houseBurden',
defaultMessage: 'Low income households spending more than 30% of income on housing',
description: 'Low income households spending more than 30% of income on housing',
},
PROX_HAZ: {
id: 'areaDetail.indicator.description.prox.haz',
defaultMessage: 'Count of hazardous waste facilities within 5 km',
description: 'Count of hazardous waste facilities within 5 km',
},
PROX_NPL: {
id: 'areaDetail.indicator.description.prox.npl',
defaultMessage: 'Proposed or listed NPL (Superfund) sites within 5 km',
description: 'Proposed or listed NPL (Superfund) sites within 5 km',
},
PROX_RMP: {
id: 'areaDetail.indicator.description.prox.npl',
defaultMessage: 'Risk Management Plan facilities within 5 km',
description: 'Risk Management Plan facilities within 5 km',
},
WASTE_WATER: {
id: 'areaDetail.indicator.description.wasteWater',
defaultMessage: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
description: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
defaultMessage: 'Toxic concentrations at stream segments within 500 meters',
description: 'Toxic concentrations at stream segments within 500 meters',
},
ASTHMA: {
id: 'areaDetail.indicator.description.asthma',
defaultMessage: 'Number of people who have been told they have asthma',
description: 'Number of people who have been told they have asthma',
},
DIABETES: {
id: 'areaDetail.indicator.description.diabetes',
defaultMessage: 'People ages 18 years and older who have diabetes other than diabetes during pregnancy',
description: 'People ages 18 years and older who have diabetes other than diabetes during pregnancy',
},
HEART_DISEASE: {
id: 'areaDetail.indicator.description.heartDisease',
defaultMessage: 'People ages 18 years and older who have been told they have heart disease',
description: 'People ages 18 years and older who have been told they have heart disease',
},
LOW_LIFE_EXPECT: {
id: 'areaDetail.indicator.description.lifeExpect',
defaultMessage: 'Average number of years of life a person can expect to live',
description: 'Average number of years of life a person can expect to live',
},
LOW_MED_INCOME: {
id: 'areaDetail.indicator.description.low.med.income',
defaultMessage: 'Median income calculated as a percent of the areas median income',
description: 'Median income calculated as a percent of the areas median income',
},
LING_ISO: {
id: 'areaDetail.indicator.description.ling.iso',
defaultMessage: `Households in which no one age 14 and over speaks English only or also speaks
a language other than English`,
description: `Households in which no one age 14 and over speaks English only or also speaks
a language other than English`,
},
UNEMPLOY: {
id: 'areaDetail.indicator.description.unemploy',
defaultMessage: 'Number of unemployed people as a percentage of the labor force',
description: 'Number of unemployed people as a percentage of the labor force',
},
POVERTY: {
id: 'areaDetail.indicator.description.poverty',
defaultMessage: `Percent of individuals in households where the household income is at or
below 100% of the federal poverty level`,
description: `Percent of individuals in households where the household income is at or
below 100% of the federal poverty level`,
},
HIGH_SKL: {
id: 'areaDetail.indicator.description.high.school',
defaultMessage: 'Percent of people ages 25 years or older whose education level is less than a high school diploma',
description: 'Percent of people ages 25 years or older whose education level is less than a high school diploma',
},
});
export const DOWNLOAD_DRAFT = {
PARAGRAPH_1: <FormattedMessage
id={'download.draft.ptag.1'}
description={'Download the draft list of communities of focus and datasets used.'}
defaultMessage={`{downloadDraft} of communities of focus and datasets used. Last updated: {dateUpdated}.`}
description={'Download the current list of communities and datasets used.'}
defaultMessage={`
{downloadDraft} of communities and datasets used (ZIP file will contain one .xlsx,
one .csv, and one .pdf, with a size of {downloadFileSize}). Last updated: {dateUpdated}.
`}
values={{
downloadDraft:
<a href={METHODOLOGY_COPY.DOWNLOAD_ZIP_URL}>
{`Download the draft list v${METHODOLOGY_COPY.VERSION_NUMBER}`}
{`Download the current list`}
</a>,
downloadDraftEs:
<a href={METHODOLOGY_COPY.DOWNLOAD_ZIP_URL}>
{'Descargue la lista preliminar'}
{`Descargue la lista preliminar`}
</a>,
dateUpdated: METHODOLOGY_COPY.DOWNLOAD_LAST_UPDATED,
dateUpdatedEs: METHODOLOGY_COPY.DOWNLOAD_LAST_UPDATED_ES,
downloadFileSize: METHODOLOGY_COPY.DOWNLOAD_FILE_SIZE,
}}
/>,
PARAGRAPH_2: <FormattedMessage
@ -428,6 +576,41 @@ export const DOWNLOAD_DRAFT = {
/>,
};
export const NOTE_ON_TERRITORIES = {
INTRO: <FormattedMessage
id={'explore.page.note.on.territories.intro'}
defaultMessage={`A note on the U.S. territories`}
description={`territories intro text`}
/>,
PARA_1: <FormattedMessage
id={'explore.page.note.on.territories.para.1'}
defaultMessage={`
The data sources described on the {dataMethLink} page are used to
identify disadvantaged communities for all 50 states and the District of Columbia. However, not all
of these data sources are currently available for the U.S. territories. The Census ACS data from
2015-2019 was used to identify disadvantaged communities for Puerto Rico. This uses the same methodology
as all 50 states and the District of Columbia for which data is available, which is all fields in
the Training and Workforce Development category.
`}
description={`territories paragraph 1`}
values={{
dataMethLink: <Link to="/methodology">Methodology & data</Link>,
}}
/>,
PARA_2: <FormattedMessage
id={'explore.page.note.on.territories.para.2'}
defaultMessage={`
For American Samoa, Northern Mariana Islands, Guam and U.S. Virgin Islands, the last reported data from
the Census Bureau is the Decennial Census from 2010. The Decennial Census data from 2010 was used
for American Samoa and Northern Mariana Islands using only the unemployment, poverty, area median
income, and high school degree achievement rate fields in the Training and Workforce Development
category of the methodology. Work is underway to identify disadvantaged communities and update the
CEJST accordingly for Guam and the U.S. Virgin Islands.
`}
description={`territories paragraph 2`}
/>,
};
export const HOW_YOU_CAN_HELP_LIST_ITEMS = {
HEADING: <FormattedMessage
id={'youCanHelpInfoText.heading'}
@ -436,34 +619,23 @@ export const HOW_YOU_CAN_HELP_LIST_ITEMS = {
/>,
LIST_ITEM_1: <FormattedMessage
id={'youCanHelpInfoText.list.item.1'}
description={'how one can help us via email'}
defaultMessage={`If you have helpful information, we would love to {rxEmailFromYou}.`}
values={{
rxEmailFromYou:
<a href={`mailto:${CONTACT_COPY.FEEDBACK_EMAIL}`}>
{'receive an email from you'}
</a>,
}}
/>,
LIST_ITEM_2: <FormattedMessage
id={'youCanHelpInfoText.list.item.2'}
description={'give us feedback on our data and methodology'}
defaultMessage={`View our {dataMeth} page and send us feedback.`}
values={{
dataMeth:
<Link to={'/methodology'}>
{'Data & methodology'}
{'Methodology and data'}
</Link>,
dataMethEs:
<Link to={'/methodology'}>
{'Datos y metodología'}
{'Metodología y datos'}
</Link>,
}}
/>,
LIST_ITEM_3: <FormattedMessage
id={'youCanHelpInfoText.list.item.3'}
LIST_ITEM_2: <FormattedMessage
id={'youCanHelpInfoText.list.item.2'}
description={'share your feedback'}
defaultMessage={`Find your community of interest and {shareFeedback}.`}
defaultMessage={`Find communities of interest and {shareFeedback}.`}
values={{
shareFeedback:
<a href={`mailto:${CONTACT_COPY.FEEDBACK_EMAIL}`}>
@ -471,5 +643,16 @@ export const HOW_YOU_CAN_HELP_LIST_ITEMS = {
</a>,
}}
/>,
LIST_ITEM_3: <FormattedMessage
id={'youCanHelpInfoText.list.item.3'}
description={'share your feedback'}
defaultMessage={`Respond to our request for information on {federalRegisterLink}.`}
values={{
federalRegisterLink:
<a href={`https://www.federalregister.gov/`}>
{'federalregister.gov'}
</a>,
}}
/>,
};

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,4 @@
import * as React from 'react';
import {Link} from 'gatsby-plugin-intl';
import {useIntl} from 'gatsby-plugin-intl';
import {Grid} from '@trussworks/react-uswds';
@ -33,12 +32,7 @@ const NotFoundPage =({location}: I404PageProps) => {
<Grid row>
<p>
{intl.formatMessage(PAGE_NOT_FOUND_COPY.PAGE.APOLOGY)}
{' '}
<span role="img" aria-label="Pensive emoji">
😔
</span>{' '}
{intl.formatMessage(PAGE_NOT_FOUND_COPY.PAGE.APOLOGY_INFO)}
{PAGE_NOT_FOUND_COPY.ERROR_MSG}
</p>
</Grid>
@ -51,11 +45,6 @@ const NotFoundPage =({location}: I404PageProps) => {
) : null}
</Grid>
<Grid>
<p>
<Link to="/">{intl.formatMessage(PAGE_NOT_FOUND_COPY.PAGE.LINK_HOME)}</Link>.
</p>
</Grid>
</J40MainGridContainer>
</Layout>

View file

@ -140,22 +140,6 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div>
</div>
</section>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
</div>
<div>
@ -261,7 +245,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
data-cy="nav-link-methodology"
href="/en/methodology"
>
Data & methodology
Methodology & data
</a>
</li>
<li
@ -277,24 +261,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<li
class="usa-nav__primary-item"
>
<div>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
<div />
</li>
</ul>
</nav>

View file

@ -140,22 +140,6 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div>
</div>
</section>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
</div>
<div>
@ -261,7 +245,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
data-cy="nav-link-methodology"
href="/en/methodology"
>
Data & methodology
Methodology & data
</a>
</li>
<li
@ -277,24 +261,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<li
class="usa-nav__primary-item"
>
<div>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
<div />
</li>
</ul>
</nav>
@ -331,14 +298,14 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
data-testid="grid"
>
<img
alt="The screening tool"
alt="Screening tool"
class="j40-aboutcard-image"
src="test-file-stub"
/>
</div>
<div
class="tablet:grid-col-9"
data-cy="the-screening-tool-block"
data-cy="screening-tool-block"
data-testid="grid"
>
<div
@ -346,13 +313,32 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
data-testid="grid"
>
<h2>
The screening tool
Screening tool
</h2>
<p>
On January 27, 2021, President Biden directed the Council on Environmental Quality (CEQ) to create a climate and economic justice screening tool. The purpose of the tool is to help Federal agencies identify disadvantaged communities and provide socioeconomic, environmental, and climate information and data to inform decisions that may affect these communities. The tool identifies disadvantaged communities as communities of focus through publicly available, nationally consistent, high-quality data.
In
<a
href="https://www.federalregister.gov/documents/2021/02/01/2021-02177/tackling-the-climate-crisis-at-home-and-abroad#:~:text=Sec.%20223.%20Justice40,40-percent%20goal."
>
Executive Order 14008
</a>
on Tackling the Climate Crisis at Home and Abroad,
President Biden directed the Council on
Environmental Quality (CEQ) to create a Climate and Economic
Justice Screening Tool. The purpose of the tool is to help
Federal agencies identify disadvantaged communities
that have been historically marginalized, underserved, and
overburdened by pollution. The tool provides
socioeconomic, environmental, and climate information
to inform decisions that may affect these communities. The tool
identifies disadvantaged communities
through publicly available, nationally consistent, high-quality
data.
</p>
<p>
The current version of the tool is in a public beta form and will be updated based on feedback from the public.
The current version of the tool is in a public beta form and will be updated based on feedback and research.
</p>
</div>
</div>
@ -376,14 +362,14 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
data-testid="grid"
>
<img
alt="The Justice40 Initiative"
alt="Justice40 Initiative"
class="j40-aboutcard-image"
src="test-file-stub"
/>
</div>
<div
class="tablet:grid-col-9"
data-cy="the-justice40-initiative-block"
data-cy="justice40-initiative-block"
data-testid="grid"
>
<div
@ -391,20 +377,22 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
data-testid="grid"
>
<h2>
The Justice40 Initiative
Justice40 Initiative
</h2>
<p>
The tool will provide important information for the Justice40 Initiative. The goal of the Justice40 Initiative is to provide 40-percent of the overall benefits of certain federal programs in seven key areas to disadvantaged communities. These seven key areas are: climate change, clean energy and energy efficiency, clean transit, affordable and sustainable housing, training and workforce development, the remediation and reduction of legacy pollution, and the development of critical clean water infrastructure.
</p>
<p>
Read more about the Justice40 Initiative in President Bidens
<a
href="https://www.whitehouse.gov/briefing-room/presidential-actions/2021/01/27/executive-order-on-tackling-the-climate-crisis-at-home-and-abroad/"
rel="noreferrer"
target="_blank"
href="https://www.federalregister.gov/documents/2021/02/01/2021-02177/tackling-the-climate-crisis-at-home-and-abroad#:~:text=Sec.%20223.%20Justice40,40-percent%20goal."
>
Executive Order 14008 on Tackling the Climate Crisis at Home and Abroad.
Executive Order 14008
</a>
on Tackling the Climate Crisis at Home and Abroad.
</p>
</div>
</div>
@ -457,7 +445,10 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
Federal program managers
</h3>
<p>
Download the screening tools draft list of communities of focus. Explore data that may be useful to your program, and provide feedback on the tool.
Download the tools current list of communities, explore data that may be useful to your
program, and provide feedback on the tool.
</p>
<div
class="j40-aboutcard-sm-link"
@ -465,7 +456,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<a
href="/en/methodology"
>
Go to data & methodology
Methodology & data
</a>
</div>
</div>
@ -509,7 +500,9 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
Community members
</h3>
<p>
Explore data about communities of focus in your area, and help provide feedback on the tool.
Explore data about communities in your area and provide feedback on the tool.
</p>
<div
class="j40-aboutcard-sm-link"

File diff suppressed because it is too large Load diff

View file

@ -47,7 +47,7 @@ const IndexPage = ({location}: IndexPageProps) => {
header={intl.formatMessage(ABOUT_COPY.PAGE.HEADING_1)}>
<>
<p>
{intl.formatMessage(ABOUT_COPY.PAGE.HEADING1_DESCRIPTION1)}
{ABOUT_COPY.HEADING_1.DESCRIPTION_1}
</p>
<p>
{intl.formatMessage(ABOUT_COPY.PAGE.HEADING1_DESCRIPTION2)}
@ -66,11 +66,7 @@ const IndexPage = ({location}: IndexPageProps) => {
{intl.formatMessage(ABOUT_COPY.PAGE.HEADING2_DESCRIPTION1)}
</p>
<p>
{intl.formatMessage(ABOUT_COPY.PAGE.HEADING2_DESCRIPTION2)}
{' '}
<a href={ABOUT_COPY.EXEC_ORDER_LINK} target={'_blank'} rel="noreferrer">
{intl.formatMessage(ABOUT_COPY.PAGE.PRESIDENT_LINK_LABEL)}
</a>
{ABOUT_COPY.HEADING_2.DESCRIPTION_2}
</p>
</>
</AboutCard>

View file

@ -2,11 +2,14 @@ import * as React from 'react';
import {Grid} from '@trussworks/react-uswds';
import {useIntl} from 'gatsby-plugin-intl';
import Categories from '../components/Categories';
import DatasetContainer from '../components/DatasetContainer';
import DownloadPacket from '../components/DownloadPacket';
import J40MainGridContainer from '../components/J40MainGridContainer';
import MethodologyFormula from '../components/MethodologyFormula';
import Layout from '../components/layout';
import ScoreStepsList from '../components/scoreStepsList';
import LowIncome from '../components/LowIncome';
// import ScoreStepsList from '../components/scoreStepsList';
import * as METHODOLOGY_COPY from '../data/copy/methodology';
@ -14,7 +17,6 @@ interface MethodPageProps {
location: Location;
}
// markup
const IndexPage = ({location}: MethodPageProps) => {
const intl = useIntl();
@ -25,29 +27,44 @@ const IndexPage = ({location}: MethodPageProps) => {
<h1>{intl.formatMessage(METHODOLOGY_COPY.PAGE.HEADING)}</h1>
{/* First column */}
<Grid row gap className={'j40-mb-5'}>
<Grid col={12} tablet={{col: 6}}>
<Grid col={12} tablet={{col: 8}}>
<section>
<p>
{intl.formatMessage(METHODOLOGY_COPY.PAGE.DESCRIPTION)}
</p>
</section>
{/* Formula section */}
<MethodologyFormula />
{/* Category description */}
<section className={`j40-mt-7`}>
<p>
{intl.formatMessage(METHODOLOGY_COPY.PAGE.CATEGORY_TEXT)}
</p>
</section>
</Grid>
<Grid col={12} tablet={{col: 6}}>
{/* Second column */}
<Grid col={12} tablet={{col: 4}}>
<DownloadPacket />
<LowIncome />
</Grid>
</Grid>
</J40MainGridContainer>
<Categories />
<DatasetContainer/>
<J40MainGridContainer>
{/* <J40MainGridContainer>
<Grid row>
<Grid col>
<ScoreStepsList/>
</Grid>
</Grid>
</J40MainGridContainer>
</J40MainGridContainer> */}
</Layout>
);
};

View file

@ -81,14 +81,19 @@ p.flush {
@include j40-element('sm', 4, 'normal', 0);
}
// 24 pixel margin-bottom
.j40-mb-3 {
@include u-margin-bottom(3)
}
// 40 pixel margin-bottom
.j40-mb-5 {
@include u-margin-bottom(5)
}
// 24 pixel margin-bottom
.j40-mb-3 {
@include u-margin-bottom(3)
// 56 pixel margin-top
.j40-mt-7 {
@include u-margin-top(7)
}
.j40-footer-ceq-font {
@ -288,15 +293,6 @@ This section will outline styles that are component specific
}
}
//Area Detail Component
p.secondary.j40-indicator {
max-width: 10rem;
@media screen and (max-width: 1024px) {
max-width: 80%;
}
}
/*
***************************************
* TIMELINE / PROCESS LIST STYLES
@ -347,10 +343,6 @@ p.secondary.j40-indicator {
width: 3rem;
}
.j40-aboutcard-link {
font-weight: bold;
}
div.j40-aboutcard-sm-link {
@include u-margin-top(2);
}

View file

@ -319,13 +319,13 @@ Or use `false` for unneeded weights.
----------------------------------------
*/
$theme-font-weight-thin: false;
$theme-font-weight-thin: 100;
$theme-font-weight-light: 300;
$theme-font-weight-normal: 400;
$theme-font-weight-medium: false;
$theme-font-weight-semibold: false;
$theme-font-weight-medium: 500;
$theme-font-weight-semibold: 600;
$theme-font-weight-bold: 700;
$theme-font-weight-heavy: false;
$theme-font-weight-heavy: 800;
// If USWDS is generating your @font-face rules,
// should we generate all available weights

View file

@ -159,7 +159,7 @@ We use Docker to install the necessary libraries in a container that can be run
To build the docker container the first time, make sure you're in the root directory of the repository and run `docker-compose build --no-cache`.
Once completed, run `docker-compose up`. Docker will spin up 3 containers: the client container, the static server container and the data container. Once all data is generated, you can see the application using a browser and navigating to `htto://localhost:8000`.
Once completed, run `docker-compose up`. Docker will spin up 3 containers: the client container, the static server container and the data container. Once all data is generated, you can see the application using a browser and navigating to `http://localhost:8000`.
If you want to run specific data tasks, you can open a terminal window, navigate to the root folder for this repository and then execute any command for the application using this format:
@ -309,7 +309,7 @@ If you update the score in any way, it is necessary to create new pickles so tha
It starts with the `data_pipeline/etl/score/tests/sample_data/score_data_initial.csv`, which is the first two rows of the `score/full/usa.csv`.
To update this file, run a full score generation and then update the file as follows:
To update this file, run a full score generation, then open a Python shell from the `data-pipeline` directory (e.g. `poetry run python3`), and then update the file with the following commands:
```
import pickle
from pathlib import Path
@ -322,6 +322,8 @@ score_initial_df = pd.read_csv(score_csv_path, dtype={"GEOID10_TRACT": "string"}
score_initial_df.to_csv(data_path / "data_pipeline" / "etl" / "score" / "tests" / "sample_data" /"score_data_initial.csv", index=False)
```
Now you can move on to updating individual pickles for the tests. Note that it is helpful to do them in this order:
We have four pickle files that correspond to expected files:
- `score_data_expected.pkl`: Initial score without counties
- `score_transformed_expected.pkl`: Intermediate score with `etl._extract_score` and `etl. _transform_score` applied. There's no file for this intermediate process, so we need to capture the pickle mid-process.

View file

@ -1,7 +1,6 @@
from pathlib import Path
import datetime
import pandas as pd
from data_pipeline.config import settings
from data_pipeline.score import field_names
@ -38,6 +37,9 @@ FULL_SCORE_CSV_FULL_PLUS_COUNTIES_FILE_PATH = (
# Score Tile CSV source path
DATA_SCORE_CSV_TILES_PATH = DATA_SCORE_CSV_DIR / "tiles"
DATA_SCORE_CSV_TILES_FILE_PATH = DATA_SCORE_CSV_TILES_PATH / "usa.csv"
DATA_SCORE_JSON_INDEX_FILE_PATH = (
DATA_SCORE_CSV_TILES_PATH / "tile_indexes.json"
)
## Tile path
DATA_SCORE_TILES_DIR = DATA_SCORE_DIR / "tiles"
@ -60,113 +62,220 @@ SCORE_DOWNLOADABLE_ZIP_FILE_PATH = (
# Column subsets
CENSUS_COUNTIES_COLUMNS = ["USPS", "GEOID", "NAME"]
TILES_SCORE_COLUMNS = [
field_names.GEOID_TRACT_FIELD,
field_names.STATE_FIELD,
field_names.COUNTY_FIELD,
field_names.TOTAL_POP_FIELD,
field_names.SCORE_D + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_D + field_names.TOP_25_PERCENTILE_SUFFIX,
field_names.SCORE_E + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_E + field_names.TOP_25_PERCENTILE_SUFFIX,
field_names.SCORE_G_COMMUNITIES,
field_names.SCORE_G,
field_names.SCORE_L_COMMUNITIES,
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HIGH_SCHOOL_ED_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LINGUISTIC_ISO_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.UNEMPLOYMENT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HOUSING_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIABETES_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ASTHMA_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HEART_DISEASE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LIFE_EXPECTANCY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.FEMA_RISK_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ENERGY_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.WASTEWATER_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LEAD_PAINT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
TILES_ROUND_NUM_DECIMALS = 2
# Tiles data: full field name, tile index name
TILES_SCORE_COLUMNS = {
field_names.GEOID_TRACT_FIELD: "GTF",
field_names.STATE_FIELD: "SF",
field_names.COUNTY_FIELD: "CF",
field_names.DIABETES_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "DF_PFS",
field_names.ASTHMA_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "AF_PFS",
field_names.HEART_DISEASE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "HDF_PFS",
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "DSF_PFS",
field_names.ENERGY_BURDEN_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "EBF_PFS",
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "EALR_PFS",
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "EBLR_PFS",
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "EPLR_PFS",
field_names.HOUSING_BURDEN_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "HBF_PFS",
field_names.LOW_LIFE_EXPECTANCY_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "LLEF_PFS",
field_names.LINGUISTIC_ISO_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "LIF_PFS",
field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "LMI_PFS",
field_names.MEDIAN_HOUSE_VALUE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "MHVF_PFS",
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "PM25F_PFS",
field_names.HIGH_SCHOOL_ED_FIELD: "HSEF",
field_names.POVERTY_LESS_THAN_100_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "P100_PFS",
field_names.POVERTY_LESS_THAN_200_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
]
+ field_names.PERCENTILE_FIELD_SUFFIX: "P200_PFS",
field_names.LEAD_PAINT_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "LPF_PFS",
field_names.NPL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "NPL_PFS",
field_names.RMP_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "RMP_PFS",
field_names.TSDF_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "TSDF_PFS",
field_names.TOTAL_POP_FIELD: "TPF",
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "TF_PFS",
field_names.UNEMPLOYMENT_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "UF_PFS",
field_names.WASTEWATER_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "WF_PFS",
field_names.L_WATER: "L_WTR",
field_names.L_WORKFORCE: "L_WKFC",
field_names.L_CLIMATE: "L_CLT",
field_names.L_ENERGY: "L_ENY",
field_names.L_TRANSPORTATION: "L_TRN",
field_names.L_HOUSING: "L_HSG",
field_names.L_POLLUTION: "L_PLN",
field_names.L_HEALTH: "L_HLTH",
field_names.SCORE_L_COMMUNITIES: "SL_C",
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX: "SL_PFS",
field_names.EXPECTED_POPULATION_LOSS_RATE_LOW_INCOME_FIELD: "EPLRLI",
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_LOW_INCOME_FIELD: "EALRLI",
field_names.EXPECTED_BUILDING_LOSS_RATE_LOW_INCOME_FIELD: "EBLRLI",
field_names.PM25_EXPOSURE_LOW_INCOME_FIELD: "PM25LI",
field_names.ENERGY_BURDEN_LOW_INCOME_FIELD: "EBLI",
field_names.DIESEL_PARTICULATE_MATTER_LOW_INCOME_FIELD: "DPMLI",
field_names.TRAFFIC_PROXIMITY_LOW_INCOME_FIELD: "TPLI",
field_names.LEAD_PAINT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD: "LPMHVLI",
field_names.HOUSING_BURDEN_LOW_INCOME_FIELD: "HBLI",
field_names.RMP_LOW_INCOME_FIELD: "RMPLI",
field_names.SUPERFUND_LOW_INCOME_FIELD: "SFLI",
field_names.HAZARDOUS_WASTE_LOW_INCOME_FIELD: "HWLI",
field_names.WASTEWATER_DISCHARGE_LOW_INCOME_FIELD: "WDLI",
field_names.DIABETES_LOW_INCOME_FIELD: "DLI",
field_names.ASTHMA_LOW_INCOME_FIELD: "ALI",
field_names.HEART_DISEASE_LOW_INCOME_FIELD: "HDLI",
field_names.LOW_LIFE_EXPECTANCY_LOW_INCOME_FIELD: "LLELI",
field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD: "LILHSE",
field_names.POVERTY_LOW_HS_EDUCATION_FIELD: "PLHSE",
field_names.LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD: "LMILHSE",
field_names.UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD: "ULHSE",
field_names.LOW_HS_EDUCATION_FIELD: "LHE",
field_names.FPL_200_SERIES: "FPL200S",
field_names.THRESHOLD_COUNT: "TC",
field_names.ISLAND_AREAS_UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD: "IAULHSE",
field_names.ISLAND_AREAS_POVERTY_LOW_HS_EDUCATION_FIELD: "ISPLHSE",
field_names.ISLAND_AREAS_LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD: "IALMILHSE",
field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD: "IALHE",
}
# columns to round floats to 2 decimals
# TODO refactor to use much smaller subset of fields we DON'T want to round
TILES_SCORE_FLOAT_COLUMNS = [
field_names.SCORE_D + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_D + field_names.TOP_25_PERCENTILE_SUFFIX,
field_names.SCORE_E + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_E + field_names.TOP_25_PERCENTILE_SUFFIX,
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_FIELD,
field_names.HIGH_SCHOOL_ED_FIELD,
field_names.LINGUISTIC_ISO_FIELD,
field_names.UNEMPLOYMENT_FIELD,
field_names.HOUSING_BURDEN_FIELD,
field_names.POVERTY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HIGH_SCHOOL_ED_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LINGUISTIC_ISO_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.UNEMPLOYMENT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HOUSING_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIABETES_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ASTHMA_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HEART_DISEASE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LIFE_EXPECTANCY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.FEMA_RISK_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ENERGY_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.WASTEWATER_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LEAD_PAINT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ENERGY_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HOUSING_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_LIFE_EXPECTANCY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LINGUISTIC_ISO_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.MEDIAN_HOUSE_VALUE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
field_names.POVERTY_LESS_THAN_100_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LEAD_PAINT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.NPL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.RMP_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TSDF_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.UNEMPLOYMENT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD,
field_names.WASTEWATER_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX,
]
TILES_ROUND_NUM_DECIMALS = 2
DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_BASIC = [
field_names.AMI_FIELD,
field_names.POVERTY_LESS_THAN_100_FPL_FIELD,
field_names.HIGH_SCHOOL_ED_FIELD,
field_names.DIABETES_FIELD,
field_names.ASTHMA_FIELD,
field_names.HEART_DISEASE_FIELD,
field_names.LIFE_EXPECTANCY_FIELD,
field_names.TRAFFIC_FIELD,
field_names.FEMA_RISK_FIELD,
field_names.ENERGY_BURDEN_FIELD,
field_names.HOUSING_BURDEN_FIELD,
field_names.WASTEWATER_FIELD,
field_names.LEAD_PAINT_FIELD,
field_names.DIESEL_FIELD,
field_names.PM25_FIELD,
field_names.TOTAL_POP_FIELD,
]
# For every indicator above, we want to include percentile and min-max normalized variants also
DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_FULL = list(
pd.core.common.flatten(
[
[p, f"{p} (percentile)"]
for p in DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_BASIC
]
)
)
# Finally we augment with the GEOID10, county, and state
DOWNLOADABLE_SCORE_COLUMNS = [
field_names.GEOID_TRACT_FIELD,
field_names.COUNTY_FIELD,
field_names.STATE_FIELD,
field_names.SCORE_G_COMMUNITIES,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_STATE_FIELD
field_names.SCORE_L_COMMUNITIES,
field_names.TOTAL_POP_FIELD,
field_names.FPL_200_SERIES,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
*DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_FULL,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_LOW_INCOME_FIELD,
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD,
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_BUILDING_LOSS_RATE_LOW_INCOME_FIELD,
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD,
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_POPULATION_LOSS_RATE_LOW_INCOME_FIELD,
field_names.ENERGY_BURDEN_FIELD,
field_names.ENERGY_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ENERGY_BURDEN_LOW_INCOME_FIELD,
field_names.PM25_FIELD,
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.PM25_EXPOSURE_LOW_INCOME_FIELD,
field_names.DIESEL_FIELD,
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIESEL_PARTICULATE_MATTER_LOW_INCOME_FIELD,
field_names.TRAFFIC_FIELD,
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TRAFFIC_PROXIMITY_LOW_INCOME_FIELD,
field_names.HOUSING_BURDEN_FIELD,
field_names.HOUSING_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HOUSING_BURDEN_LOW_INCOME_FIELD,
field_names.LEAD_PAINT_FIELD,
field_names.LEAD_PAINT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LEAD_PAINT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD,
field_names.MEDIAN_HOUSE_VALUE_FIELD,
field_names.MEDIAN_HOUSE_VALUE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TSDF_FIELD,
field_names.TSDF_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HAZARDOUS_WASTE_LOW_INCOME_FIELD,
field_names.NPL_FIELD,
field_names.NPL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SUPERFUND_LOW_INCOME_FIELD,
field_names.RMP_FIELD,
field_names.RMP_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.RMP_LOW_INCOME_FIELD,
field_names.WASTEWATER_FIELD,
field_names.WASTEWATER_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.WASTEWATER_DISCHARGE_LOW_INCOME_FIELD,
field_names.ASTHMA_FIELD,
field_names.ASTHMA_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ASTHMA_LOW_INCOME_FIELD,
field_names.DIABETES_FIELD,
field_names.DIABETES_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIABETES_LOW_INCOME_FIELD,
field_names.HEART_DISEASE_FIELD,
field_names.HEART_DISEASE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HEART_DISEASE_LOW_INCOME_FIELD,
field_names.LIFE_EXPECTANCY_FIELD,
field_names.LOW_LIFE_EXPECTANCY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_LIFE_EXPECTANCY_LOW_INCOME_FIELD,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
field_names.LINGUISTIC_ISO_FIELD,
field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD,
field_names.UNEMPLOYMENT_FIELD,
field_names.UNEMPLOYMENT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LINGUISTIC_ISO_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_LESS_THAN_100_FPL_FIELD,
field_names.POVERTY_LESS_THAN_100_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_LOW_HS_EDUCATION_FIELD,
field_names.HIGH_SCHOOL_ED_FIELD,
field_names.HIGH_SCHOOL_ED_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_HS_EDUCATION_FIELD,
field_names.THRESHOLD_COUNT,
field_names.UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD,
field_names.COMBINED_UNEMPLOYMENT_2010,
field_names.CENSUS_DECENNIAL_UNEMPLOYMENT_FIELD_2009,
field_names.COMBINED_POVERTY_LESS_THAN_100_FPL_FIELD_2010,
field_names.ISLAND_AREAS_UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_POVERTY_LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD,
]

View file

@ -1,6 +1,7 @@
import functools
from collections import namedtuple
import numpy as np
import pandas as pd
from data_pipeline.etl.base import ExtractTransformLoad
@ -253,6 +254,94 @@ class ScoreETL(ExtractTransformLoad):
f"Too many rows in the join: {len(df_to_check)} in {dataframe_descriptor}"
)
@staticmethod
def _add_percentiles_to_df(
df: pd.DataFrame,
input_column_name: str,
output_column_name_root: str,
ascending: bool = True,
) -> pd.DataFrame:
"""Creates percentiles.
One percentile will be created and returned as
f"{output_column_name_root}{field_names.PERCENTILE_FIELD_SUFFIX}".
E.g., "PM2.5 exposure (percentile)".
This will be for the entire country.
For an "apples-to-apples" comparison of urban tracts to other urban tracts,
and compare rural tracts to other rural tracts.
This percentile will be created and returned as
f"{output_column_name_root}{field_names.PERCENTILE_URBAN_RURAL_FIELD_SUFFIX}".
E.g., "PM2.5 exposure (percentile urban/rural)".
This field exists for every tract, but for urban tracts this value will be the
percentile compared to other urban tracts, and for rural tracts this value
will be the percentile compared to other rural tracts.
Specific methdology:
1. Decide a methodology for confirming whether a tract counts as urban or
rural. Currently in the codebase, we use Geocorr to identify the % rural of
a tract, and mark the tract as rural if the percentage is >50% and urban
otherwise. This may or may not be the right methodology.
2. Once tracts are marked as urban or rural, create one percentile rank
that only ranks urban tracts, and one percentile rank that only ranks rural
tracts.
3. Combine into a single field.
`output_column_name_root` is different from `input_column_name` to enable the
reverse percentile use case. In that use case, `input_column_name` may be
something like "3rd grade reading proficiency" and `output_column_name_root`
may be something like "Low 3rd grade reading proficiency".
"""
# Create the "basic" percentile.
df[
f"{output_column_name_root}"
f"{field_names.PERCENTILE_FIELD_SUFFIX}"
] = df[input_column_name].rank(pct=True, ascending=ascending)
# Create the urban/rural percentiles.
urban_rural_percentile_fields_to_combine = []
for (urban_or_rural_string, urban_heuristic_bool) in [
("urban", True),
("rural", False),
]:
# Create a field with only those values
this_category_only_value_field = (
f"{input_column_name} (value {urban_or_rural_string} only)"
)
df[this_category_only_value_field] = np.where(
df[field_names.URBAN_HEURISTIC_FIELD] == urban_heuristic_bool,
df[input_column_name],
None,
)
# Calculate the percentile for only this category
this_category_only_percentile_field = (
f"{output_column_name_root} "
f"(percentile {urban_or_rural_string} only)"
)
df[this_category_only_percentile_field] = df[
this_category_only_value_field
].rank(
pct=True,
# Set ascending to the parameter value.
ascending=ascending,
)
# Add the field name to this list. Later, we'll combine this list.
urban_rural_percentile_fields_to_combine.append(
this_category_only_percentile_field
)
# Combine both urban and rural into one field:
df[
f"{output_column_name_root}{field_names.PERCENTILE_URBAN_RURAL_FIELD_SUFFIX}"
] = df[urban_rural_percentile_fields_to_combine].mean(
axis=1, skipna=True
)
return df
# TODO Move a lot of this to the ETL part of the pipeline
def _prepare_initial_df(self) -> pd.DataFrame:
logger.info("Preparing initial dataframe")
@ -315,9 +404,7 @@ class ScoreETL(ExtractTransformLoad):
field_names.POVERTY_LESS_THAN_150_FPL_FIELD,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD,
field_names.AMI_FIELD,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
field_names.MEDIAN_INCOME_FIELD,
field_names.LIFE_EXPECTANCY_FIELD,
field_names.ENERGY_BURDEN_FIELD,
field_names.FEMA_RISK_FIELD,
field_names.URBAN_HEURISTIC_FIELD,
@ -350,7 +437,6 @@ class ScoreETL(ExtractTransformLoad):
field_names.CENSUS_UNEMPLOYMENT_FIELD_2010,
field_names.CENSUS_POVERTY_LESS_THAN_100_FPL_FIELD_2010,
field_names.CENSUS_DECENNIAL_TOTAL_POPULATION_FIELD_2009,
field_names.CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009,
field_names.EXTREME_HEAT_FIELD,
field_names.HEALTHY_FOOD_FIELD,
field_names.IMPENETRABLE_SURFACES_FIELD,
@ -379,7 +465,19 @@ class ScoreETL(ExtractTransformLoad):
ReversePercentile(
field_name=field_names.READING_FIELD,
low_field_name=field_names.LOW_READING_FIELD,
)
),
ReversePercentile(
field_name=field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
low_field_name=field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
),
ReversePercentile(
field_name=field_names.LIFE_EXPECTANCY_FIELD,
low_field_name=field_names.LOW_LIFE_EXPECTANCY_FIELD,
),
ReversePercentile(
field_name=field_names.CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009,
low_field_name=field_names.LOW_CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009,
),
]
columns_to_keep = (
@ -393,11 +491,14 @@ class ScoreETL(ExtractTransformLoad):
df_copy[numeric_columns] = df_copy[numeric_columns].apply(pd.to_numeric)
# Convert all columns to numeric and do math
for col in numeric_columns:
# Calculate percentiles
df_copy[f"{col}{field_names.PERCENTILE_FIELD_SUFFIX}"] = df_copy[
col
].rank(pct=True)
for numeric_column in numeric_columns:
df_copy = self._add_percentiles_to_df(
df=df_copy,
input_column_name=numeric_column,
# For this use case, the input name and output name root are the same.
output_column_name_root=numeric_column,
ascending=True,
)
# Min-max normalization:
# (
@ -409,16 +510,12 @@ class ScoreETL(ExtractTransformLoad):
# Maximum of all values
# - minimum of all values
# )
min_value = df_copy[col].min(skipna=True)
min_value = df_copy[numeric_column].min(skipna=True)
max_value = df_copy[col].max(skipna=True)
max_value = df_copy[numeric_column].max(skipna=True)
logger.info(
f"For data set {col}, the min value is {min_value} and the max value is {max_value}."
)
df_copy[f"{col}{field_names.MIN_MAX_FIELD_SUFFIX}"] = (
df_copy[col] - min_value
df_copy[f"{numeric_column}{field_names.MIN_MAX_FIELD_SUFFIX}"] = (
df_copy[numeric_column] - min_value
) / (max_value - min_value)
# Create reversed percentiles for these fields
@ -427,11 +524,11 @@ class ScoreETL(ExtractTransformLoad):
# For instance, for 3rd grade reading level (score from 0-500),
# calculate reversed percentiles and give the result the name
# `Low 3rd grade reading level (percentile)`.
df_copy[
f"{reverse_percentile.low_field_name}"
f"{field_names.PERCENTILE_FIELD_SUFFIX}"
] = df_copy[reverse_percentile.field_name].rank(
pct=True, ascending=False
df_copy = self._add_percentiles_to_df(
df=df_copy,
input_column_name=reverse_percentile.field_name,
output_column_name_root=reverse_percentile.low_field_name,
ascending=False,
)
# Special logic: create a combined population field.

View file

@ -3,10 +3,12 @@ import pandas as pd
import geopandas as gpd
from data_pipeline.etl.base import ExtractTransformLoad
from data_pipeline.etl.score import constants
from data_pipeline.etl.sources.census.etl_utils import (
check_census_data_source,
)
from data_pipeline.etl.score.etl_utils import check_score_data_source
from data_pipeline.score import field_names
from data_pipeline.utils import get_module_logger
logger = get_module_logger(__name__)
@ -31,9 +33,19 @@ class GeoScoreETL(ExtractTransformLoad):
self.DATA_PATH / "census" / "geojson" / "us.json"
)
self.TARGET_SCORE_NAME = "Definition L (percentile)"
# Import the shortened name for Score L percentile ("SL_PFS") that's used on the
# tiles.
self.TARGET_SCORE_SHORT_FIELD = constants.TILES_SCORE_COLUMNS[
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX
]
self.TARGET_SCORE_RENAME_TO = "L_SCORE"
# Import the shortened name for tract ("GTF") that's used on the tiles.
self.TRACT_SHORT_FIELD = constants.TILES_SCORE_COLUMNS[
field_names.GEOID_TRACT_FIELD
]
self.GEOMETRY_FIELD_NAME = "geometry"
self.NUMBER_OF_BUCKETS = 10
self.geojson_usa_df: gpd.GeoDataFrame
@ -57,45 +69,52 @@ class GeoScoreETL(ExtractTransformLoad):
logger.info("Reading US GeoJSON (~6 minutes)")
self.geojson_usa_df = gpd.read_file(
self.CENSUS_USA_GEOJSON,
dtype={"GEOID10": "string"},
usecols=["GEOID10", "geometry"],
dtype={self.GEOID_FIELD_NAME: "string"},
usecols=[self.GEOID_FIELD_NAME, self.GEOMETRY_FIELD_NAME],
low_memory=False,
)
self.geojson_usa_df.head()
logger.info("Reading score CSV")
self.score_usa_df = pd.read_csv(
self.TILE_SCORE_CSV,
dtype={self.GEOID_TRACT_FIELD_NAME: "string"},
dtype={self.TRACT_SHORT_FIELD: "string"},
low_memory=False,
)
def transform(self) -> None:
# rename GEOID10_TRACT to GEOID10 on score to allow merging with Census GeoJSON
# Rename GEOID10_TRACT to GEOID10 on score to allow merging with Census GeoJSON
self.score_usa_df.rename(
columns={self.GEOID_TRACT_FIELD_NAME: "GEOID10"},
columns={self.TRACT_SHORT_FIELD: self.GEOID_FIELD_NAME},
inplace=True,
)
logger.info("Pruning Census GeoJSON")
fields = ["GEOID10", "geometry"]
fields = [self.GEOID_FIELD_NAME, self.GEOMETRY_FIELD_NAME]
self.geojson_usa_df = self.geojson_usa_df[fields]
logger.info("Merging and compressing score CSV with USA GeoJSON")
self.geojson_score_usa_high = self.score_usa_df.merge(
self.geojson_usa_df, on="GEOID10", how="left"
self.geojson_usa_df, on=self.GEOID_FIELD_NAME, how="left"
)
self.geojson_score_usa_high = gpd.GeoDataFrame(
self.geojson_score_usa_high, crs="EPSG:4326"
)
logger.info(f"Columns: {self.geojson_score_usa_high.columns}")
usa_simplified = self.geojson_score_usa_high[
["GEOID10", self.TARGET_SCORE_NAME, "geometry"]
[
self.GEOID_FIELD_NAME,
self.TARGET_SCORE_SHORT_FIELD,
self.GEOMETRY_FIELD_NAME,
]
].reset_index(drop=True)
usa_simplified.rename(
columns={self.TARGET_SCORE_NAME: self.TARGET_SCORE_RENAME_TO},
columns={
self.TARGET_SCORE_SHORT_FIELD: self.TARGET_SCORE_RENAME_TO
},
inplace=True,
)
@ -104,7 +123,7 @@ class GeoScoreETL(ExtractTransformLoad):
usa_tracts = gpd.GeoDataFrame(
usa_tracts,
columns=[self.TARGET_SCORE_RENAME_TO, "geometry"],
columns=[self.TARGET_SCORE_RENAME_TO, self.GEOMETRY_FIELD_NAME],
crs="EPSG:4326",
)
@ -122,7 +141,7 @@ class GeoScoreETL(ExtractTransformLoad):
self.geojson_score_usa_low = gpd.GeoDataFrame(
compressed,
columns=[self.TARGET_SCORE_RENAME_TO, "geometry"],
columns=[self.TARGET_SCORE_RENAME_TO, self.GEOMETRY_FIELD_NAME],
crs="EPSG:4326",
)
@ -135,7 +154,7 @@ class GeoScoreETL(ExtractTransformLoad):
) -> gpd.GeoDataFrame:
# The tract identifier is the first 11 digits of the GEOID
block_group_df["tract"] = block_group_df.apply(
lambda row: row["GEOID10"][0:11], axis=1
lambda row: row[self.GEOID_FIELD_NAME][0:11], axis=1
)
state_tracts = block_group_df.dissolve(by="tract", aggfunc="mean")
return state_tracts
@ -160,7 +179,7 @@ class GeoScoreETL(ExtractTransformLoad):
[
self.TARGET_SCORE_RENAME_TO,
f"{self.TARGET_SCORE_RENAME_TO}_bucket",
"geometry",
self.GEOMETRY_FIELD_NAME,
]
].reset_index(drop=True)
state_dissolve = state_attr.dissolve(
@ -173,11 +192,13 @@ class GeoScoreETL(ExtractTransformLoad):
) -> gpd.GeoDataFrame:
compressed = []
for i in range(num_buckets):
for j in range(len(state_bucketed_df["geometry"][i].geoms)):
for j in range(
len(state_bucketed_df[self.GEOMETRY_FIELD_NAME][i].geoms)
):
compressed.append(
[
state_bucketed_df[self.TARGET_SCORE_RENAME_TO][i],
state_bucketed_df["geometry"][i].geoms[j],
state_bucketed_df[self.GEOMETRY_FIELD_NAME][i].geoms[j],
]
)
return compressed

View file

@ -1,5 +1,7 @@
from pathlib import Path
import json
import pandas as pd
from data_pipeline.etl.base import ExtractTransformLoad
from data_pipeline.utils import get_module_logger, zip_files
from data_pipeline.score import field_names
@ -198,16 +200,37 @@ class PostScoreETL(ExtractTransformLoad):
self, score_county_state_merged_df: pd.DataFrame
) -> pd.DataFrame:
logger.info("Rounding Decimals")
score_tiles = score_county_state_merged_df[
constants.TILES_SCORE_COLUMNS
]
# grab all the keys from tiles score columns
tiles_score_column_titles = list(constants.TILES_SCORE_COLUMNS.keys())
# filter the columns on full score
score_tiles = score_county_state_merged_df[tiles_score_column_titles]
# round decimals
decimals = pd.Series(
[constants.TILES_ROUND_NUM_DECIMALS]
* len(constants.TILES_SCORE_FLOAT_COLUMNS),
index=constants.TILES_SCORE_FLOAT_COLUMNS,
)
score_tiles = score_tiles.round(decimals)
return score_tiles.round(decimals)
# create indexes
score_tiles = score_tiles.rename(
columns=constants.TILES_SCORE_COLUMNS,
inplace=False,
)
# write the json map to disk
inverse_tiles_columns = {
v: k for k, v in constants.TILES_SCORE_COLUMNS.items()
} # reverse dict
index_file_path = constants.DATA_SCORE_JSON_INDEX_FILE_PATH
index_file_path.parent.mkdir(parents=True, exist_ok=True)
with open(index_file_path, "w", encoding="utf-8") as fp:
json.dump(inverse_tiles_columns, fp)
return score_tiles
def _create_downloadable_data(
self, score_county_state_merged_df: pd.DataFrame

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show more