Merge branch 'usds:main' into main

This commit is contained in:
Saran Ahluwalia 2021-12-07 16:01:52 -05:00 committed by GitHub
commit e30b32fe07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 851 additions and 260 deletions

View file

@ -12,24 +12,26 @@ 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) => {
return Math.round(percentile * 100);
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) => {
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 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 {
@ -40,11 +42,13 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
const intl = useIntl();
const [isCommunityFocus, setIsCommunityFocus] = React.useState<boolean>(true);
const score = properties[constants.SCORE_PROPERTY_HIGH] as number;
const blockGroup = properties[constants.GEOID_PROPERTY];
const population = properties[constants.TOTAL_POPULATION];
const countyName = properties[constants.COUNTY_NAME];
const stateName = properties[constants.STATE_NAME];
console.log("Area Detail properies: ", properties);
const score = properties[constants.SCORE_PROPERTY_HIGH] ? properties[constants.SCORE_PROPERTY_HIGH] as number : 0;
const blockGroup = properties[constants.GEOID_PROPERTY] ? properties[constants.GEOID_PROPERTY] : "N/A";
const population = properties[constants.TOTAL_POPULATION] ? properties[constants.TOTAL_POPULATION] : "N/A";
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 ) {
@ -65,77 +69,92 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
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],
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],
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],
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],
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],
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],
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],
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],
value: properties[constants.ENERGY_PERCENTILE] ?
properties[constants.ENERGY_PERCENTILE] : 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],
value: properties[constants.PM25_PERCENTILE] ?
properties[constants.PM25_PERCENTILE] : 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],
value: properties[constants.LEAD_PAINT_PERCENTILE] ?
properties[constants.LEAD_PAINT_PERCENTILE] : 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],
value: properties[constants.TRAFFIC_PERCENTILE] ?
properties[constants.TRAFFIC_PERCENTILE] : 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],
value: properties[constants.WASTEWATER_PERCENTILE] ?
properties[constants.WASTEWATER_PERCENTILE] : 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],
value: properties[constants.FEMA_PERCENTILE] ?
properties[constants.FEMA_PERCENTILE] : 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],
value: properties[constants.HEART_PERCENTILE] ?
properties[constants.HEART_PERCENTILE] : 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],
value: properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] ?
properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] : null,
};

View file

@ -19,7 +19,7 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
County:
</span>
<span>
undefined
N/A
</span>
</li>
<li>
@ -27,7 +27,7 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
State:
</span>
<span>
undefined
N/A
</span>
</li>
<li>
@ -134,11 +134,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Asthma
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -156,11 +154,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Diabetes
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -178,11 +174,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Diesel particulate matter
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -200,11 +194,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Energy burden
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -222,11 +214,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
FEMA Risk Index
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -244,11 +234,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Heart disease
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -288,11 +276,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Lead paint
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -310,11 +296,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Life expectancy
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -332,11 +316,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
PM2.5
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -354,11 +336,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Traffic proximity and volume
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>
@ -376,11 +356,9 @@ exports[`rendering of the AreaDetail checks if various text fields are visible 1
Wastewater discharge
</h4>
<div>
NaN
N/A
<sup>
<span>
th
</span>
<span />
</sup>
</div>
</div>

View file

@ -1,6 +1,6 @@
/* eslint-disable no-unused-vars */
// External Libs:
import React, {MouseEvent, useRef, useState, useMemo} from 'react';
import React, {useRef, useState, useMemo} from 'react';
import {Map, MapboxGeoJSONFeature, LngLatBoundsLike} from 'maplibre-gl';
import ReactMapGL, {
MapEvent,
@ -136,29 +136,6 @@ const J40Map = ({location}: IJ40Interface) => {
});
};
const onClickTerritoryFocusButton = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
const buttonID = event.target && (event.target as HTMLElement).id;
switch (buttonID) {
case '48':
goToPlace(constants.LOWER_48_BOUNDS);
break;
case 'AK':
goToPlace(constants.ALASKA_BOUNDS);
break;
case 'HI':
goToPlace(constants.HAWAII_BOUNDS);
break;
case 'PR':
goToPlace(constants.PUERTO_RICO_BOUNDS);
break;
default:
break;
}
};
const onTransitionStart = () => {
setTransitionInProgress(true);
};
@ -273,7 +250,7 @@ const J40Map = ({location}: IJ40Interface) => {
onClick={onClickGeolocate}
/> : ''}
{geolocationInProgress ? <div>Geolocation in progress...</div> : ''}
<TerritoryFocusControl onClickTerritoryFocusButton={onClickTerritoryFocusButton}/>
<TerritoryFocusControl goToPlace={goToPlace}/>
{'fs' in flags ? <FullscreenControl className={styles.fullscreenControl}/> :'' }
</ReactMapGL>

View file

@ -1,21 +1,53 @@
import {useIntl} from 'gatsby-plugin-intl';
import React, {MouseEventHandler} from 'react';
import {_useMapControl as useMapControl} from 'react-map-gl';
import React from 'react';
import {LngLatBoundsLike} from 'mapbox-gl';
import * as styles from './territoryFocusControl.module.scss';
import * as EXPLORE_COPY from '../data/copy/explore';
import * as constants from '../data/constants';
interface ITerritoryFocusControl {
onClickTerritoryFocusButton: MouseEventHandler<HTMLButtonElement>;
goToPlace(bounds: LngLatBoundsLike): void;
}
const TerritoryFocusControl = ({onClickTerritoryFocusButton}: ITerritoryFocusControl) => {
const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
const intl = useIntl();
const {containerRef} = useMapControl({
// @ts-ignore // Types have not caught up yet, see https://github.com/visgl/react-map-gl/issues/1492
onClick: onClickTerritoryFocusButton,
});
const onClickTerritoryFocusButton = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
const buttonID = event.target && (event.target as HTMLElement).id;
switch (buttonID) {
case '48':
goToPlace(constants.LOWER_48_BOUNDS);
break;
case 'AK':
goToPlace(constants.ALASKA_BOUNDS);
break;
case 'HI':
goToPlace(constants.HAWAII_BOUNDS);
break;
case 'PR':
goToPlace(constants.PUERTO_RICO_BOUNDS);
break;
case 'GU':
goToPlace(constants.GUAM_BOUNDS);
break;
case 'AS':
goToPlace(constants.AMERICAN_SAMOA_BOUNDS);
break;
case 'MP':
goToPlace(constants.MARIANA_ISLAND_BOUNDS);
break;
case 'VI':
goToPlace(constants.US_VIRGIN_ISLANDS_BOUNDS);
break;
default:
break;
}
};
const territories = [
{
@ -34,6 +66,22 @@ const TerritoryFocusControl = ({onClickTerritoryFocusButton}: ITerritoryFocusCon
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.AS_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.AS_LONG),
},
{
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),
},
];
// the offset for this array should map the territories variable
const territoriesIconClassName = [
@ -41,16 +89,20 @@ const TerritoryFocusControl = ({onClickTerritoryFocusButton}: ITerritoryFocusCon
'mapboxgl-ctrl-zoom-to-ak',
'mapboxgl-ctrl-zoom-to-hi',
'mapboxgl-ctrl-zoom-to-pr',
'mapboxgl-ctrl-zoom-to-gu',
'mapboxgl-ctrl-zoom-to-as',
'mapboxgl-ctrl-zoom-to-mp',
'mapboxgl-ctrl-zoom-to-vi',
];
return (
<div ref={containerRef} className={styles.territoryFocusContainer}>
<div className={styles.territoryFocusContainer}>
<div className={'mapboxgl-ctrl mapboxgl-ctrl-group'}>
{territories.map((territory, index) =>
<button
id={territory.short}
key={territory.short}
onClick={onClickTerritoryFocusButton}
onClick={(e) => onClickTerritoryFocusButton(e)}
className={'mapboxgl-ctrl-icon ' + territoriesIconClassName[index]}
aria-label={intl.formatMessage(
{

View file

@ -121,6 +121,11 @@ export const AMERICAN_SAMOA_BOUNDS: LngLatBoundsLike = [
[-168.1433, -11.046934],
];
export const US_VIRGIN_ISLANDS_BOUNDS: LngLatBoundsLike = [
[-65.5782239, 17.6739145],
[-64.2704123, 18.7495796],
];
export const DEFAULT_CENTER = [32.4687126, -86.502136];
// Opacity

View file

@ -57,6 +57,16 @@ export const MAP = defineMessages({
defaultMessage: 'Zoom in to the state or regional level to see prioritized communities on the map.',
description: 'zoom warning on map',
},
SEARCH_PLACEHOLDER: {
id: 'map.search.placeholder.text',
defaultMessage: 'Enter a city, state or ZIP',
description: 'placeholder text for search',
},
SEARCH_RESULTS_EMPTY_MESSAGE: {
id: 'map.search.results.empty.text',
defaultMessage: 'No location found. Please try another location.',
description: 'text displaying message for no search results found',
},
LOWER48_SHORT: {
id: 'map.territoryFocus.lower48.short',
defaultMessage: '48',
@ -97,15 +107,45 @@ export const MAP = defineMessages({
defaultMessage: 'Puerto Rico',
description: 'The full name indicating the bounds of Puerto Rico',
},
SEARCH_PLACEHOLDER: {
id: 'map.search.placeholder.text',
defaultMessage: 'Enter a city, state or ZIP',
description: 'placeholder text for search',
GU_SHORT: {
id: 'map.territoryFocus.guam.short',
defaultMessage: 'GU',
description: 'The abbreviated name indicating the bounds of Guam',
},
SEARCH_RESULTS_EMPTY_MESSAGE: {
id: 'map.search.results.empty.text',
defaultMessage: 'No location found. Please try another location.',
description: 'text displaying message for no search results found',
GU_LONG: {
id: 'map.territoryFocus.guam.long',
defaultMessage: 'Guam',
description: 'The full name indicating the bounds of Guam',
},
AS_SHORT: {
id: 'map.territoryFocus.american.samoa.short',
defaultMessage: 'AS',
description: 'The abbreviated name indicating the bounds of American Somoa',
},
AS_LONG: {
id: 'map.territoryFocus.american.samoa.long',
defaultMessage: 'American Somoa',
description: 'The full name indicating the bounds of American Somoa',
},
MP_SHORT: {
id: 'map.territoryFocus.commonwealth.nmp.short',
defaultMessage: 'MP',
description: 'The abbreviated name indicating the bounds of Commonwealth of Northern Mariana Islands',
},
MP_LONG: {
id: 'map.territoryFocus.commonwealth.nmp.long',
defaultMessage: 'Commonwealth of Northern Mariana Islands',
description: 'The full name indicating the bounds of Commonwealth of Northern Mariana Islands',
},
VI_SHORT: {
id: 'map.territoryFocus.us.virgin.islands.short',
defaultMessage: 'VI',
description: 'The abbreviated name indicating the bounds of US Virgin Islands',
},
VI_LONG: {
id: 'map.territoryFocus.us.virgin.islands.long',
defaultMessage: 'US Virgin Islands',
description: 'The full name indicating the bounds of US Virgin Islands',
},
});

View file

@ -1 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M-.54.11h24v24h-24Z" transform="translate(0.54 -0.11)" style="fill:none"/><path d="M11.74,13.46H10.18V16H8.61V13.46H4.67V12.31L8.79,6.83h1.39v5.28h1.56ZM8.61,9.22,6.34,12.11H8.61Z" transform="translate(0.54 -0.11)"/><path d="M12.74,13.77a2.23,2.23,0,0,1,.15-.83,2.62,2.62,0,0,1,.39-.67,3.51,3.51,0,0,1,.55-.54,6.51,6.51,0,0,1,.64-.43,4,4,0,0,1-1.08-.89A2,2,0,0,1,13,9.12a2.21,2.21,0,0,1,.22-.95,2.54,2.54,0,0,1,.62-.77,3.13,3.13,0,0,1,.94-.5,3.8,3.8,0,0,1,1.19-.18,4,4,0,0,1,1.1.15,3.14,3.14,0,0,1,.9.44,2.15,2.15,0,0,1,.61.7,1.82,1.82,0,0,1,.22.9,2.26,2.26,0,0,1-.4,1.34,3.46,3.46,0,0,1-1.08,1,4,4,0,0,1,1.24.95,2.07,2.07,0,0,1,.46,1.4,2.21,2.21,0,0,1-.25,1.06,2.54,2.54,0,0,1-.68.81A3.14,3.14,0,0,1,17,16a3.86,3.86,0,0,1-1.23.19A3.93,3.93,0,0,1,14.6,16a3.14,3.14,0,0,1-1-.48,2.15,2.15,0,0,1-.65-.76A2,2,0,0,1,12.74,13.77Zm4.63-.22A1,1,0,0,0,17.2,13a1.82,1.82,0,0,0-.42-.41,4.34,4.34,0,0,0-.56-.33c-.2-.1-.4-.2-.59-.31a2.45,2.45,0,0,0-1,.74,1.37,1.37,0,0,0-.35.85,1.08,1.08,0,0,0,.41.9,1.9,1.9,0,0,0,1.16.32,1.78,1.78,0,0,0,1.09-.31A1,1,0,0,0,17.37,13.55ZM14.58,9.2a1,1,0,0,0,.13.53,1.53,1.53,0,0,0,.35.37,2.64,2.64,0,0,0,.49.29l.55.26a2.13,2.13,0,0,0,.41-.26,1.88,1.88,0,0,0,.35-.34,2.2,2.2,0,0,0,.26-.39,1,1,0,0,0,.09-.41,1,1,0,0,0-.11-.47,1.15,1.15,0,0,0-.31-.35,1.19,1.19,0,0,0-.42-.21,1.59,1.59,0,0,0-.48-.07,1.51,1.51,0,0,0-.53.09,1.46,1.46,0,0,0-.41.23,1.13,1.13,0,0,0-.28.34A.92.92,0,0,0,14.58,9.2Z" transform="translate(0.54 -0.11)"/></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h24v24H0L0,0z"/>
<g>
<path d="M8.9,15.6v-2.3H5v-1.1l3.5-5.5h1.9V12h1.1v1.2h-1.1v2.3H8.9z M6.5,12h2.3v-2c0-0.3,0-0.6,0-0.9c0-0.4,0-0.7,0-0.9H8.9
C8.8,8.4,8.7,8.6,8.5,8.9C8.4,9.1,8.3,9.3,8.2,9.6L6.5,12z"/>
<path d="M15.5,15.8c-0.6,0-1.1-0.1-1.6-0.3c-0.5-0.2-0.8-0.5-1.1-0.9s-0.4-0.8-0.4-1.3c0-0.6,0.2-1,0.5-1.4
c0.3-0.4,0.7-0.7,1.1-0.9V11c-0.3-0.2-0.6-0.5-0.9-0.9c-0.2-0.3-0.4-0.7-0.4-1.2c0-0.5,0.1-0.9,0.3-1.3s0.6-0.6,1-0.8
c0.4-0.2,0.9-0.3,1.4-0.3c0.8,0,1.4,0.2,1.9,0.7c0.5,0.4,0.7,1,0.7,1.7c0,0.4-0.1,0.8-0.4,1.2c-0.3,0.4-0.5,0.6-0.8,0.9V11
c0.4,0.2,0.8,0.5,1.1,0.9c0.3,0.4,0.5,0.8,0.5,1.4c0,0.5-0.1,0.9-0.4,1.2c-0.3,0.4-0.6,0.7-1.1,0.9C16.6,15.6,16.1,15.8,15.5,15.8z
M15.5,14.6c0.4,0,0.8-0.1,1.1-0.4s0.4-0.6,0.4-1c0-0.3-0.1-0.6-0.3-0.8c-0.2-0.2-0.4-0.4-0.8-0.5s-0.7-0.3-1.1-0.5
c-0.3,0.2-0.5,0.4-0.7,0.7c-0.2,0.3-0.3,0.6-0.3,0.9c0,0.4,0.2,0.8,0.5,1.1S15,14.6,15.5,14.6z M16.1,10.6c0.2-0.2,0.4-0.5,0.6-0.7
s0.2-0.5,0.2-0.8c0-0.4-0.1-0.7-0.3-1c-0.2-0.3-0.6-0.4-1-0.4c-0.3,0-0.6,0.1-0.9,0.3c-0.2,0.2-0.4,0.5-0.4,0.9
c0,0.3,0.1,0.5,0.2,0.7c0.2,0.2,0.4,0.4,0.7,0.5C15.4,10.3,15.7,10.4,16.1,10.6z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M-.54.11h24v24h-24Z" transform="translate(0.54 -0.11)" style="fill:none"/><path d="M9.34,14H6.62L6,16H4.22L7.16,6.9H8.94L11.86,16H10ZM7,12.55H9l-.92-3H8Z" transform="translate(0.54 -0.11)"/><path d="M15.39,12.2h-.93V16H12.68V6.9h1.78v3.72h.93L17.47,6.9h2l-2.57,4.39L19.82,16h-2.2Z" transform="translate(0.54 -0.11)"/></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h24v24H0L0,0z"/>
<g>
<path d="M3.8,15.6l3-9.2h1.9l3,9.2H9.9l-0.7-2.5H6.1l-0.7,2.5H3.8z M6.8,10.7l-0.3,1.1h2.3l-0.3-1.1c-0.1-0.5-0.3-1-0.4-1.5
C8,8.7,7.8,8.2,7.7,7.7H7.6c-0.1,0.5-0.2,1-0.4,1.5S7,10.2,6.8,10.7z"/>
<path d="M12.8,15.6V6.4h1.6v4.2h0l3.3-4.2h1.8L16.7,10l3.3,5.6h-1.8l-2.5-4.3l-1.3,1.6v2.7H12.8z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 415 B

After

Width:  |  Height:  |  Size: 747 B

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h24v24H0L0,0z"/>
<g>
<path d="M4.2,15.6l3-9.2h1.9l3,9.2h-1.7l-0.7-2.5H6.6l-0.7,2.5H4.2z M7.3,10.7L7,11.8h2.3L9,10.7c-0.1-0.5-0.3-1-0.4-1.5
c-0.1-0.5-0.3-1-0.4-1.5H8.1C8,8.2,7.8,8.7,7.7,9.2S7.4,10.2,7.3,10.7z"/>
<path d="M15.9,15.8c-0.6,0-1.2-0.1-1.8-0.3c-0.6-0.2-1.1-0.6-1.5-1l1-1.1c0.3,0.3,0.7,0.6,1.1,0.7c0.4,0.2,0.8,0.3,1.3,0.3
c0.5,0,0.9-0.1,1.2-0.3c0.3-0.2,0.4-0.5,0.4-0.9c0-0.4-0.1-0.7-0.4-0.8c-0.3-0.2-0.6-0.3-1-0.5l-1.3-0.5c-0.3-0.1-0.6-0.3-0.9-0.5
c-0.3-0.2-0.5-0.5-0.7-0.8C13.1,9.7,13,9.3,13,8.8c0-0.5,0.1-0.9,0.4-1.3c0.3-0.4,0.6-0.7,1.1-0.9c0.5-0.2,1-0.3,1.6-0.3
c0.5,0,1,0.1,1.5,0.3c0.5,0.2,0.9,0.5,1.3,0.8l-0.8,1c-0.3-0.2-0.6-0.4-0.9-0.6c-0.3-0.1-0.7-0.2-1.1-0.2c-0.4,0-0.8,0.1-1,0.3
c-0.3,0.2-0.4,0.5-0.4,0.8c0,0.2,0.1,0.4,0.2,0.6c0.1,0.2,0.3,0.3,0.5,0.4c0.2,0.1,0.5,0.2,0.7,0.3l1.2,0.5c0.5,0.2,1,0.5,1.3,0.9
c0.3,0.4,0.5,0.9,0.5,1.6c0,0.5-0.1,0.9-0.4,1.4c-0.3,0.4-0.6,0.7-1.1,1C17.2,15.6,16.6,15.8,15.9,15.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h24v24H0L0,0z"/>
<g>
<path d="M7.9,15.8c-0.8,0-1.6-0.2-2.2-0.5c-0.7-0.4-1.2-0.9-1.5-1.6C3.8,12.9,3.6,12,3.6,11c0-1,0.2-1.9,0.6-2.6
c0.4-0.7,0.9-1.3,1.6-1.6s1.4-0.6,2.2-0.6c0.7,0,1.2,0.1,1.7,0.4c0.5,0.2,0.8,0.5,1.1,0.8L9.8,8.5C9.6,8.3,9.3,8.1,9,7.9
C8.8,7.8,8.4,7.7,8,7.7C7.2,7.7,6.5,8,6,8.6C5.5,9.2,5.2,10,5.2,11c0,1,0.2,1.9,0.7,2.5s1.2,0.9,2.1,0.9c0.3,0,0.5,0,0.7-0.1
c0.2-0.1,0.4-0.2,0.6-0.3v-2H7.6v-1.3h3.2v4c-0.3,0.3-0.7,0.6-1.2,0.8C9.1,15.7,8.5,15.8,7.9,15.8z"/>
<path d="M16.3,15.8c-0.7,0-1.3-0.1-1.8-0.4s-0.9-0.7-1.2-1.3c-0.3-0.6-0.4-1.4-0.4-2.4V6.4h1.6v5.3c0,0.7,0.1,1.2,0.2,1.6
c0.2,0.4,0.4,0.7,0.7,0.8c0.3,0.2,0.6,0.2,1,0.2c0.4,0,0.7-0.1,1-0.2c0.3-0.2,0.5-0.4,0.7-0.8c0.2-0.4,0.2-0.9,0.2-1.6V6.4h1.6v5.1
c0,1-0.1,1.8-0.4,2.4c-0.3,0.6-0.7,1.1-1.2,1.3C17.6,15.6,17,15.8,16.3,15.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M-.54.11h24v24h-24Z" transform="translate(0.54 -0.11)" style="fill:none"/><path d="M9.64,12.19H6.7V16H4.92V6.9H6.7v3.7H9.64V6.9h1.78V16H9.64Z" transform="translate(0.54 -0.11)"/><path d="M12.63,14.41h2.31V8.48H12.63V6.9H19V8.48H16.72v5.93H19V16h-6.4Z" transform="translate(0.54 -0.11)"/></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h24v24H0L0,0z"/>
<g>
<path d="M6.4,15.6V6.4H8v3.7h3.7V6.4h1.6v9.2h-1.6v-4.1H8v4.1H6.4z"/>
<path d="M15.7,15.6V6.4h1.6v9.2H15.7z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 566 B

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h24v24H0L0,0z"/>
<g>
<path d="M3.7,15.6V6.4h1.8l1.6,4.5c0.1,0.3,0.2,0.6,0.3,0.9c0.1,0.3,0.2,0.6,0.3,0.9h0.1c0.1-0.3,0.2-0.6,0.3-0.9
c0.1-0.3,0.2-0.6,0.3-0.9L10,6.4h1.8v9.2h-1.5v-4.2c0-0.3,0-0.6,0-0.9c0-0.3,0.1-0.7,0.1-1c0-0.3,0.1-0.7,0.1-0.9h-0.1l-0.7,2.1
l-1.5,4.1h-1l-1.5-4.1L5,8.5H5C5,8.8,5,9.1,5.1,9.4c0,0.3,0.1,0.7,0.1,1c0,0.3,0,0.7,0,0.9v4.2H3.7z"/>
<path d="M14.1,15.6V6.4H17c0.7,0,1.3,0.1,1.8,0.3c0.5,0.2,1,0.5,1.3,0.9s0.5,1,0.5,1.7c0,0.7-0.2,1.2-0.5,1.7
c-0.3,0.4-0.7,0.8-1.2,1c-0.5,0.2-1.1,0.3-1.8,0.3h-1.3v3.4H14.1z M15.8,10.9h1.2c1.3,0,2-0.6,2-1.7c0-0.6-0.2-1-0.5-1.2
c-0.3-0.2-0.9-0.3-1.5-0.3h-1.1V10.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M-.54.11h24v24h-24Z" transform="translate(0.54 -0.11)" style="fill:none"/><path d="M5.25,7a12.1,12.1,0,0,1,1.2-.16c.44,0,.87-.06,1.31-.06a7.88,7.88,0,0,1,1.36.11,3.3,3.3,0,0,1,1.22.44,2.54,2.54,0,0,1,.89.92,3.05,3.05,0,0,1,.35,1.54,3.15,3.15,0,0,1-.3,1.43,2.79,2.79,0,0,1-.79,1,3.24,3.24,0,0,1-1.13.56,4.69,4.69,0,0,1-1.3.18H7.55l-.31,0-.21,0V16H5.25ZM7.9,8.3l-.49,0a2.53,2.53,0,0,0-.38,0v3l.16,0,.22,0H7.8a3.23,3.23,0,0,0,.69-.07,1.61,1.61,0,0,0,.62-.24,1.18,1.18,0,0,0,.43-.48,1.75,1.75,0,0,0,.17-.82,1.38,1.38,0,0,0-.16-.71,1.19,1.19,0,0,0-.41-.45,1.57,1.57,0,0,0-.58-.23A2.84,2.84,0,0,0,7.9,8.3Z" transform="translate(0.54 -0.11)"/><path d="M12.71,7l.64-.1L14,6.82l.67,0h.6a6.54,6.54,0,0,1,1.25.12,3.37,3.37,0,0,1,1.11.42,2.33,2.33,0,0,1,.78.81,2.55,2.55,0,0,1,.29,1.26,3.93,3.93,0,0,1-.11,1,3,3,0,0,1-.31.72,2.23,2.23,0,0,1-.47.54,4.33,4.33,0,0,1-.61.41l2.2,4H17.39l-1.84-3.56H14.49V16H12.71Zm2.73,1.36-.54,0a1.81,1.81,0,0,0-.41.05v2.64h.7a2.06,2.06,0,0,0,1.21-.34,1.3,1.3,0,0,0,.47-1.12,1.14,1.14,0,0,0-.37-.91A1.49,1.49,0,0,0,15.44,8.35Z" transform="translate(0.54 -0.11)"/></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h24v24H0L0,0z"/>
<g>
<path d="M4.7,15.6V6.4h2.9c0.7,0,1.3,0.1,1.8,0.3c0.5,0.2,1,0.5,1.3,0.9s0.5,1,0.5,1.7c0,0.7-0.2,1.2-0.5,1.7
c-0.3,0.4-0.7,0.8-1.2,1c-0.5,0.2-1.1,0.3-1.8,0.3H6.4v3.4H4.7z M6.4,10.9h1.2c1.3,0,2-0.6,2-1.7c0-0.6-0.2-1-0.5-1.2
C8.7,7.8,8.2,7.7,7.5,7.7H6.4V10.9z"/>
<path d="M12.9,15.6V6.4H16c0.6,0,1.2,0.1,1.7,0.3s0.9,0.5,1.2,0.8c0.3,0.4,0.4,0.9,0.4,1.6c0,0.7-0.2,1.3-0.5,1.7
c-0.3,0.4-0.8,0.7-1.3,0.9l2.2,3.8h-1.8l-2-3.6h-1.4v3.6H12.9z M14.5,10.7h1.3c0.6,0,1.1-0.1,1.4-0.4c0.3-0.3,0.5-0.7,0.5-1.2
c0-0.5-0.2-0.9-0.5-1.1c-0.3-0.2-0.8-0.3-1.4-0.3h-1.3V10.7z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1,018 B

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<path class="st0" d="M0,0h24v24H0L0,0z"/>
<g>
<path d="M9,15.6L6.1,6.4h1.7L9.1,11c0.1,0.5,0.3,1,0.4,1.5c0.1,0.5,0.3,1,0.4,1.5H10c0.2-0.5,0.3-1,0.4-1.5c0.1-0.5,0.2-1,0.4-1.5
l1.3-4.6h1.7l-2.8,9.2H9z"/>
<path d="M14.8,15.6V6.4h1.6v9.2H14.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 654 B

View file

@ -262,6 +262,30 @@ This section will outline styles that are component specific
background-image: url("../images/mapbtn-PR.svg");
}
}
button.mapboxgl-ctrl-zoom-to-gu {
.mapboxgl-ctrl-icon {
background-image: url("../images/mapbtn-GU.svg");
}
}
button.mapboxgl-ctrl-zoom-to-as {
.mapboxgl-ctrl-icon {
background-image: url("../images/mapbtn-AS.svg");
}
}
button.mapboxgl-ctrl-zoom-to-mp {
.mapboxgl-ctrl-icon {
background-image: url("../images/mapbtn-MP.svg");
}
}
button.mapboxgl-ctrl-zoom-to-vi {
.mapboxgl-ctrl-icon {
background-image: url("../images/mapbtn-VI.svg");
}
}
}
//Area Detail Component

View file

@ -49,6 +49,11 @@ DATASET_LIST = [
"module_dir": "geocorr",
"class_name": "GeoCorrETL",
},
{
"name": "child_opportunity_index",
"module_dir": "child_opportunity_index",
"class_name": "ChildOpportunityIndex",
},
{
"name": "mapping_inequality",
"module_dir": "mapping_inequality",

View file

@ -4,6 +4,8 @@ import datetime
import pandas as pd
from data_pipeline.config import settings
from data_pipeline.score import field_names
# Base Paths
DATA_PATH = Path(settings.APP_ROOT) / "data"
TMP_PATH = DATA_PATH / "tmp"
@ -59,88 +61,92 @@ SCORE_DOWNLOADABLE_ZIP_FILE_PATH = (
# Column subsets
CENSUS_COUNTIES_COLUMNS = ["USPS", "GEOID", "NAME"]
TILES_SCORE_COLUMNS = [
"GEOID10_TRACT",
"State Name",
"County Name",
"Total population",
"Score D (percentile)",
"Score D (top 25th percentile)",
"Score E (percentile)",
"Score E (top 25th percentile)",
"Score G (communities)",
"Score G",
"Definition L (communities)",
"Definition L (percentile)",
"Poverty (Less than 200% of federal poverty line) (percentile)",
"Percent individuals age 25 or over with less than high school degree (percentile)",
"Linguistic isolation (percent) (percentile)",
"Unemployed civilians (percent) (percentile)",
"Housing burden (percent) (percentile)",
"Diagnosed diabetes among adults aged >=18 years (percentile)",
"Current asthma among adults aged >=18 years (percentile)",
"Coronary heart disease among adults aged >=18 years (percentile)",
"Life expectancy (years) (percentile)",
"Traffic proximity and volume (percentile)",
"FEMA Risk Index Expected Annual Loss Score (percentile)",
"Energy burden (percentile)",
"Wastewater discharge (percentile)",
"Percent pre-1960s housing (lead paint indicator) (percentile)",
"Diesel particulate matter (percentile)",
"Particulate matter (PM2.5) (percentile)",
"Median household income (% of AMI) (percentile)",
"Percent of individuals < 200% Federal Poverty Line (percentile)",
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,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
]
# columns to round floats to 2 decimals
TILES_SCORE_FLOAT_COLUMNS = [
"Score D (percentile)",
"Score D (top 25th percentile)",
"Score E (percentile)",
"Score E (top 25th percentile)",
"Definition L (percentile)",
"Poverty (Less than 200% of federal poverty line)",
"Percent individuals age 25 or over with less than high school degree",
"Linguistic isolation (percent)",
"Unemployed civilians (percent)",
"Housing burden (percent)",
"Poverty (Less than 200% of federal poverty line) (percentile)",
"Percent individuals age 25 or over with less than high school degree (percentile)",
"Linguistic isolation (percent) (percentile)",
"Unemployed civilians (percent) (percentile)",
"Housing burden (percent) (percentile)",
"Diagnosed diabetes among adults aged >=18 years (percentile)",
"Current asthma among adults aged >=18 years (percentile)",
"Coronary heart disease among adults aged >=18 years (percentile)",
"Life expectancy (years) (percentile)",
"Traffic proximity and volume (percentile)",
"FEMA Risk Index Expected Annual Loss Score (percentile)",
"Energy burden (percentile)",
"Wastewater discharge (percentile)",
"Percent pre-1960s housing (lead paint indicator) (percentile)",
"Diesel particulate matter (percentile)",
"Particulate matter (PM2.5) (percentile)",
"Median household income (% of AMI) (percentile)",
"Percent of individuals < 200% Federal Poverty Line (percentile)",
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.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
]
TILES_ROUND_NUM_DECIMALS = 2
DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_BASIC = [
"Area Median Income (State or metropolitan)",
"Percent of individuals < 100% Federal Poverty Line",
"Percent individuals age 25 or over with less than high school degree",
"Diagnosed diabetes among adults aged >=18 years",
"Current asthma among adults aged >=18 years",
"Coronary heart disease among adults aged >=18 years",
"Life expectancy (years)",
"Traffic proximity and volume",
"FEMA Risk Index Expected Annual Loss Score",
"Energy burden",
"Housing burden (percent)",
"Wastewater discharge",
"Percent pre-1960s housing (lead paint indicator)",
"Diesel particulate matter",
"Particulate matter (PM2.5)",
"Total population",
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
@ -155,11 +161,12 @@ DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_FULL = list(
# Finally we augment with the GEOID10, county, and state
DOWNLOADABLE_SCORE_COLUMNS = [
"GEOID10_TRACT",
"County Name",
"State Name",
"Score G (communities)",
"Median household income (% of AMI)",
"Median household income (% of state median household income) (percentile)",
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.PERCENTILE_FIELD_SUFFIX,
*DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_FULL,
]

View file

@ -1,4 +1,6 @@
import functools
from collections import namedtuple
import pandas as pd
from data_pipeline.etl.base import ExtractTransformLoad
@ -29,6 +31,7 @@ class ScoreETL(ExtractTransformLoad):
self.persistent_poverty_df: pd.DataFrame
self.census_decennial_df: pd.DataFrame
self.census_2010_df: pd.DataFrame
self.child_opportunity_index_df: pd.DataFrame
def extract(self) -> None:
logger.info("Loading data sets from disk.")
@ -162,6 +165,19 @@ class ScoreETL(ExtractTransformLoad):
low_memory=False,
)
# Load COI data
child_opportunity_index_csv = (
constants.DATA_PATH
/ "dataset"
/ "child_opportunity_index"
/ "usa.csv"
)
self.child_opportunity_index_df = pd.read_csv(
child_opportunity_index_csv,
dtype={self.GEOID_TRACT_FIELD_NAME: "string"},
low_memory=False,
)
def _join_tract_dfs(self, census_tract_dfs: list) -> pd.DataFrame:
logger.info("Joining Census Tract dataframes")
@ -255,6 +271,7 @@ class ScoreETL(ExtractTransformLoad):
self.census_acs_median_incomes_df,
self.census_decennial_df,
self.census_2010_df,
self.child_opportunity_index_df,
]
# Sanity check each data frame before merging.
@ -305,7 +322,7 @@ class ScoreETL(ExtractTransformLoad):
field_names.FEMA_RISK_FIELD,
field_names.URBAN_HEURISTIC_FIELD,
field_names.AIR_TOXICS_CANCER_RISK_FIELD,
field_names.RESPITORY_HAZARD_FIELD,
field_names.RESPIRATORY_HAZARD_FIELD,
field_names.DIESEL_FIELD,
field_names.PM25_FIELD,
field_names.OZONE_FIELD,
@ -323,6 +340,7 @@ class ScoreETL(ExtractTransformLoad):
field_names.HIGH_SCHOOL_ED_FIELD,
field_names.UNEMPLOYMENT_FIELD,
field_names.MEDIAN_HOUSE_VALUE_FIELD,
field_names.COLLEGE_ATTENDANCE_FIELD,
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD,
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD,
@ -333,6 +351,9 @@ class ScoreETL(ExtractTransformLoad):
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,
]
non_numeric_columns = [
@ -340,7 +361,32 @@ class ScoreETL(ExtractTransformLoad):
field_names.PERSISTENT_POVERTY_FIELD,
]
columns_to_keep = non_numeric_columns + numeric_columns
# For some columns, high values are "good", so we want to reverse the percentile
# so that high values are "bad" and any scoring logic can still check if it's
# >= some threshold.
# TODO: Add more fields here.
# https://github.com/usds/justice40-tool/issues/970
ReversePercentile = namedtuple(
typename="ReversePercentile",
field_names=["field_name", "low_field_name"],
)
reverse_percentiles = [
# This dictionary follows the format:
# <field name> : <field name for low values>
# for instance, 3rd grade reading level : Low 3rd grade reading level.
# This low field will not exist yet, it is only calculated for the
# percentile.
ReversePercentile(
field_name=field_names.READING_FIELD,
low_field_name=field_names.LOW_READING_FIELD,
)
]
columns_to_keep = (
non_numeric_columns
+ numeric_columns
+ [rp.field_name for rp in reverse_percentiles]
)
df_copy = df[columns_to_keep].copy()
@ -375,6 +421,19 @@ class ScoreETL(ExtractTransformLoad):
df_copy[col] - min_value
) / (max_value - min_value)
# Create reversed percentiles for these fields
for reverse_percentile in reverse_percentiles:
# Calculate reverse percentiles
# 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
)
# Special logic: create a combined population field.
# We sometimes run analytics on "population", and this makes a single field
# that is either the island area's population in 2009 or the state's

View file

@ -276,12 +276,15 @@ class PostScoreETL(ExtractTransformLoad):
inplace=False,
)
logger.info("Writing downloadable csv")
downloadable_df_copy.to_csv(csv_path, index=False)
logger.info("Writing downloadable excel")
downloadable_df_copy.to_excel(excel_path, index=False)
logger.info("Writing downloadable csv")
downloadable_df_copy[self.GEOID_TRACT_FIELD_NAME] = (
'"' + downloadable_df_copy[self.GEOID_TRACT_FIELD_NAME] + '"'
)
downloadable_df_copy.to_csv(csv_path, index=False)
logger.info("Compressing files")
files_to_compress = [csv_path, excel_path, pdf_path]
zip_files(zip_path, files_to_compress)

View file

@ -114,6 +114,27 @@ class CensusACSETL(ExtractTransformLoad):
)
self.HIGH_SCHOOL_ED_FIELD = "Percent individuals age 25 or over with less than high school degree"
# College attendance fields
self.COLLEGE_ATTENDANCE_TOTAL_POPULATION_ASKED = (
"B14004_001E" # Estimate!!Total
)
self.COLLEGE_ATTENDANCE_MALE_ENROLLED_PUBLIC = "B14004_003E" # Estimate!!Total!!Male!!Enrolled in public college or graduate school
self.COLLEGE_ATTENDANCE_MALE_ENROLLED_PRIVATE = "B14004_008E" # Estimate!!Total!!Male!!Enrolled in private college or graduate school
self.COLLEGE_ATTENDANCE_FEMALE_ENROLLED_PUBLIC = "B14004_019E" # Estimate!!Total!!Female!!Enrolled in public college or graduate school
self.COLLEGE_ATTENDANCE_FEMALE_ENROLLED_PRIVATE = "B14004_024E" # Estimate!!Total!!Female!!Enrolled in private college or graduate school
self.COLLEGE_ATTENDANCE_FIELDS = [
self.COLLEGE_ATTENDANCE_TOTAL_POPULATION_ASKED,
self.COLLEGE_ATTENDANCE_MALE_ENROLLED_PUBLIC,
self.COLLEGE_ATTENDANCE_MALE_ENROLLED_PRIVATE,
self.COLLEGE_ATTENDANCE_FEMALE_ENROLLED_PUBLIC,
self.COLLEGE_ATTENDANCE_FEMALE_ENROLLED_PRIVATE,
]
self.COLLEGE_ATTENDANCE_FIELD = (
"Percent enrollment in college or graduate school"
)
self.RE_FIELDS = [
"B02001_001E",
"B02001_002E",
@ -156,15 +177,30 @@ class CensusACSETL(ExtractTransformLoad):
self.STATE_GEOID_FIELD_NAME = "GEOID2"
self.COLUMNS_TO_KEEP = (
[
self.GEOID_TRACT_FIELD_NAME,
self.UNEMPLOYED_FIELD_NAME,
self.LINGUISTIC_ISOLATION_FIELD_NAME,
self.MEDIAN_INCOME_FIELD_NAME,
self.POVERTY_LESS_THAN_100_PERCENT_FPL_FIELD_NAME,
self.POVERTY_LESS_THAN_150_PERCENT_FPL_FIELD_NAME,
self.POVERTY_LESS_THAN_200_PERCENT_FPL_FIELD_NAME,
self.MEDIAN_HOUSE_VALUE_FIELD_NAME,
self.HIGH_SCHOOL_ED_FIELD,
self.COLLEGE_ATTENDANCE_FIELD,
]
+ self.RE_OUTPUT_FIELDS
+ [self.PERCENT_PREFIX + field for field in self.RE_OUTPUT_FIELDS]
)
self.df: pd.DataFrame
def extract(self) -> None:
# Define the variables to retrieve
variables = (
[
# Income field
self.MEDIAN_INCOME_FIELD,
# House value
self.MEDIAN_HOUSE_VALUE_FIELD,
]
+ self.EMPLOYMENT_FIELDS
@ -172,6 +208,7 @@ class CensusACSETL(ExtractTransformLoad):
+ self.POVERTY_FIELDS
+ self.EDUCATIONAL_FIELDS
+ self.RE_FIELDS
+ self.COLLEGE_ATTENDANCE_FIELDS
)
self.df = retrieve_census_acs_data(
@ -308,6 +345,14 @@ class CensusACSETL(ExtractTransformLoad):
df["B03003_003E"] / df["B03003_001E"]
)
# Calculate college attendance:
df[self.COLLEGE_ATTENDANCE_FIELD] = (
df[self.COLLEGE_ATTENDANCE_MALE_ENROLLED_PUBLIC]
+ df[self.COLLEGE_ATTENDANCE_MALE_ENROLLED_PRIVATE]
+ df[self.COLLEGE_ATTENDANCE_FEMALE_ENROLLED_PUBLIC]
+ df[self.COLLEGE_ATTENDANCE_FEMALE_ENROLLED_PRIVATE]
) / df[self.COLLEGE_ATTENDANCE_TOTAL_POPULATION_ASKED]
# Save results to self.
self.df = df
@ -317,23 +362,7 @@ class CensusACSETL(ExtractTransformLoad):
# mkdir census
self.OUTPUT_PATH.mkdir(parents=True, exist_ok=True)
columns_to_include = (
[
self.GEOID_TRACT_FIELD_NAME,
self.UNEMPLOYED_FIELD_NAME,
self.LINGUISTIC_ISOLATION_FIELD_NAME,
self.MEDIAN_INCOME_FIELD_NAME,
self.POVERTY_LESS_THAN_100_PERCENT_FPL_FIELD_NAME,
self.POVERTY_LESS_THAN_150_PERCENT_FPL_FIELD_NAME,
self.POVERTY_LESS_THAN_200_PERCENT_FPL_FIELD_NAME,
self.MEDIAN_HOUSE_VALUE_FIELD_NAME,
self.HIGH_SCHOOL_ED_FIELD,
]
+ self.RE_OUTPUT_FIELDS
+ [self.PERCENT_PREFIX + field for field in self.RE_OUTPUT_FIELDS]
)
self.df[columns_to_include].to_csv(
self.df[self.COLUMNS_TO_KEEP].to_csv(
path_or_buf=self.OUTPUT_PATH / "usa.csv", index=False
)

View file

@ -0,0 +1,120 @@
from pathlib import Path
import pandas as pd
from data_pipeline.etl.base import ExtractTransformLoad
from data_pipeline.score import field_names
from data_pipeline.utils import get_module_logger, unzip_file_from_url
logger = get_module_logger(__name__)
class ChildOpportunityIndex(ExtractTransformLoad):
"""ETL Child Opportunity Index data.
COI compiles a number of useful data sets. In the future, we could pull these
data sets in directly from their original creators.
Data dictionary available when you download zip from `self.COI_FILE_URL`.
Data source overview: https://data.diversitydatakids.org/dataset/coi20-child-opportunity-index-2-0-database.
Full technical documents: https://www.diversitydatakids.org/sites/default/files/2020-02/ddk_coi2.0_technical_documentation_20200212.pdf.
Github repo: https://github.com/diversitydatakids/COI/
"""
def __init__(self):
self.COI_FILE_URL = (
"https://data.diversitydatakids.org/datastore/zip/f16fff12-b1e5-4f60-85d3-"
"3a0ededa30a0?format=csv"
)
self.OUTPUT_PATH: Path = (
self.DATA_PATH / "dataset" / "child_opportunity_index"
)
self.TRACT_INPUT_COLUMN_NAME = "geoid"
self.EXTREME_HEAT_INPUT_FIELD = "HE_HEAT"
self.HEALTHY_FOOD_INPUT_FIELD = "HE_FOOD"
self.IMPENETRABLE_SURFACES_INPUT_FIELD = "HE_GREEN"
self.READING_INPUT_FIELD = "ED_READING"
# Constants for output
self.COLUMNS_TO_KEEP = [
self.GEOID_TRACT_FIELD_NAME,
field_names.EXTREME_HEAT_FIELD,
field_names.HEALTHY_FOOD_FIELD,
field_names.IMPENETRABLE_SURFACES_FIELD,
field_names.READING_FIELD,
]
self.raw_df: pd.DataFrame
self.output_df: pd.DataFrame
def extract(self) -> None:
logger.info("Starting 51MB data download.")
unzip_file_from_url(
file_url=self.COI_FILE_URL,
download_path=self.TMP_PATH,
unzipped_file_path=self.TMP_PATH / "child_opportunity_index",
)
self.raw_df = pd.read_csv(
filepath_or_buffer=self.TMP_PATH
/ "child_opportunity_index"
/ "raw.csv",
# The following need to remain as strings for all of their digits, not get
# converted to numbers.
dtype={
self.TRACT_INPUT_COLUMN_NAME: "string",
},
low_memory=False,
)
def transform(self) -> None:
logger.info("Starting transforms.")
output_df = self.raw_df.rename(
columns={
self.TRACT_INPUT_COLUMN_NAME: self.GEOID_TRACT_FIELD_NAME,
self.EXTREME_HEAT_INPUT_FIELD: field_names.EXTREME_HEAT_FIELD,
self.HEALTHY_FOOD_INPUT_FIELD: field_names.HEALTHY_FOOD_FIELD,
self.IMPENETRABLE_SURFACES_INPUT_FIELD: field_names.IMPENETRABLE_SURFACES_FIELD,
self.READING_INPUT_FIELD: field_names.READING_FIELD,
}
)
# Sanity check the tract field.
if len(output_df[self.GEOID_TRACT_FIELD_NAME].str.len().unique()) != 1:
raise ValueError("Wrong tract length.")
# COI has two rows per tract: one for 2010 and one for 2015.
output_df = output_df[output_df["year"] == 2015]
# Convert percents from 0-100 to 0-1 to standardize with our other fields.
percent_fields_to_convert = [
field_names.HEALTHY_FOOD_FIELD,
field_names.IMPENETRABLE_SURFACES_FIELD,
]
for percent_field_to_convert in percent_fields_to_convert:
output_df[percent_field_to_convert] = (
output_df[percent_field_to_convert] / 100
)
self.output_df = output_df
def validate(self) -> None:
logger.info("Validating data.")
pass
def load(self) -> None:
logger.info("Saving CSV")
self.OUTPUT_PATH.mkdir(parents=True, exist_ok=True)
self.output_df[self.COLUMNS_TO_KEEP].to_csv(
path_or_buf=self.OUTPUT_PATH / "usa.csv", index=False
)

View file

@ -8,6 +8,12 @@ logger = get_module_logger(__name__)
class EJSCREENETL(ExtractTransformLoad):
"""Load EJSCREEN data.
Data dictionary:
https://gaftp.epa.gov/EJSCREEN/2019/2019_EJSCREEN_columns_explained.csv
"""
def __init__(self):
self.EJSCREEN_FTP_URL = "https://edap-arcgiscloud-data-commons.s3.amazonaws.com/EJSCREEN2020/EJSCREEN_Tract_2020_USPR.csv.zip"
self.EJSCREEN_CSV = self.TMP_PATH / "EJSCREEN_Tract_2020_USPR.csv"
@ -19,7 +25,7 @@ class EJSCREENETL(ExtractTransformLoad):
field_names.TOTAL_POP_FIELD,
# pylint: disable=duplicate-code
field_names.AIR_TOXICS_CANCER_RISK_FIELD,
field_names.RESPITORY_HAZARD_FIELD,
field_names.RESPIRATORY_HAZARD_FIELD,
field_names.DIESEL_FIELD,
field_names.PM25_FIELD,
field_names.OZONE_FIELD,
@ -61,7 +67,7 @@ class EJSCREENETL(ExtractTransformLoad):
# but I think that's the direction we'd like to move all ETL classes. - LMB
"ACSTOTPOP": field_names.TOTAL_POP_FIELD,
"CANCER": field_names.AIR_TOXICS_CANCER_RISK_FIELD,
"RESP": field_names.RESPITORY_HAZARD_FIELD,
"RESP": field_names.RESPIRATORY_HAZARD_FIELD,
"DSLPM": field_names.DIESEL_FIELD,
"PM25": field_names.PM25_FIELD,
"OZONE": field_names.OZONE_FIELD,

View file

@ -479,7 +479,7 @@
"comparison_fields = [\n",
" field_names.POVERTY_LESS_THAN_100_FPL_FIELD,\n",
" field_names.POVERTY_LESS_THAN_200_FPL_FIELD,\n",
" field_names.MEDIAN_INCOME_PERCENT_AMI_FIELD,\n",
" field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,\n",
" field_names.LINGUISTIC_ISO_FIELD,\n",
" field_names.UNEMPLOYMENT_FIELD,\n",
" field_names.HIGH_SCHOOL_ED_FIELD,\n",

View file

@ -1,6 +1,12 @@
# Suffixes
PERCENTILE_FIELD_SUFFIX = " (percentile)"
MIN_MAX_FIELD_SUFFIX = " (min-max normalized)"
TOP_25_PERCENTILE_SUFFIX = " (top 25th percentile)"
# Geographic field names
GEOID_TRACT_FIELD = "GEOID10_TRACT"
STATE_FIELD = "State Name"
COUNTY_FIELD = "County Name"
# Score file field names
SCORE_A = "Score A"
@ -21,6 +27,7 @@ SCORE_I = "Score I"
SCORE_I_COMMUNITIES = "Score I (communities)"
SCORE_K = "NMTC (communities)"
SCORE_K_COMMUNITIES = "Score K (communities)"
SCORE_L = "Definition L"
SCORE_L_COMMUNITIES = "Definition L (communities)"
L_CLIMATE = "Climate Factor (Definition L)"
L_ENERGY = "Energy Factor (Definition L)"
@ -45,7 +52,6 @@ POVERTY_LESS_THAN_150_FPL_FIELD = (
POVERTY_LESS_THAN_100_FPL_FIELD = (
"Percent of individuals < 100% Federal Poverty Line"
)
MEDIAN_INCOME_PERCENT_AMI_FIELD = "Median household income (% of AMI)"
STATE_MEDIAN_INCOME_FIELD = (
"Median household income (State; 2019 inflation-adjusted dollars)"
)
@ -57,6 +63,8 @@ MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD = "Median household income (% of AMI)"
PERSISTENT_POVERTY_FIELD = "Persistent Poverty Census Tract"
AMI_FIELD = "Area Median Income (State or metropolitan)"
COLLEGE_ATTENDANCE_FIELD = "Percent enrollment in college or graduate school"
# Climate
FEMA_RISK_FIELD = "FEMA Risk Index Expected Annual Loss Score"
EXPECTED_BUILDING_LOSS_RATE_FIELD = (
@ -81,6 +89,7 @@ RMP_FIELD = "Proximity to Risk Management Plan (RMP) facilities"
TSDF_FIELD = "Proximity to TSDF sites"
NPL_FIELD = "Proximity to NPL sites"
AIR_TOXICS_CANCER_RISK_FIELD = "Air toxics cancer risk"
RESPIRATORY_HAZARD_FIELD = "Respiratory hazard index"
# Housing
HOUSING_BURDEN_FIELD = "Housing burden (percent)"
@ -96,7 +105,6 @@ DIABETES_FIELD = "Diagnosed diabetes among adults aged >=18 years"
ASTHMA_FIELD = "Current asthma among adults aged >=18 years"
HEART_DISEASE_FIELD = "Coronary heart disease among adults aged >=18 years"
LIFE_EXPECTANCY_FIELD = "Life expectancy (years)"
RESPITORY_HAZARD_FIELD = "Respiratory hazard index"
CANCER_FIELD = "Cancer (excluding skin cancer) among adults aged >=18 years"
HEALTH_INSURANCE_FIELD = (
"Current lack of health insurance among adults aged 18-64 years"
@ -200,30 +208,65 @@ HOLC_GRADE_D_TRACT_50_PERCENT_FIELD: str = "Tract is >50% HOLC Grade D"
HOLC_GRADE_D_TRACT_75_PERCENT_FIELD: str = "Tract is >75% HOLC Grade D"
# Child Opportunity Index data
# Summer days with maximum temperature above 90F.
EXTREME_HEAT_FIELD = "Summer days above 90F"
# Percentage households without a car located further than a half-mile from the
# nearest supermarket.
HEALTHY_FOOD_FIELD = "Percent low access to healthy food"
# Percentage impenetrable surface areas such as rooftops, roads or parking lots.
IMPENETRABLE_SURFACES_FIELD = "Percent impenetrable surface areas"
# Percentage third graders scoring proficient on standardized reading tests,
# converted to NAEP scale score points.
READING_FIELD = "Third grade reading proficiency"
LOW_READING_FIELD = "Low third grade reading proficiency"
# Names for individual factors being exceeded
# Climate Change
EXPECTED_POPULATION_LOSS_RATE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for expected population loss rate and is low income"
EXPECTED_AGRICULTURE_LOSS_RATE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for expected agriculture loss rate and is low income"
EXPECTED_BUILDING_LOSS_RATE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for expected building loss rate and is low income"
EXTREME_HEAT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for summer days above 90F and "
f"the median house value is less than {MEDIAN_HOUSE_VALUE_PERCENTILE}th "
f"percentile and is low income"
)
# Clean energy and efficiency
PM25_EXPOSURE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for PM2.5 exposure and is low income"
ENERGY_BURDEN_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for energy burden and is low income"
# Clean transportation
DIESEL_PARTICULATE_MATTER_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for diesel particulate matter and is low income"
TRAFFIC_PROXIMITY_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for traffic proximity and is low income"
# Affordable and Sustainable Housing
LEAD_PAINT_MEDIAN_HOME_VALUE_LOW_INCOME_FIELD = (
LEAD_PAINT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for lead paint and"
" the median house value is less than {MEDIAN_HOUSE_VALUE_PERCENTILE}th percentile and is low income"
f" the median house value is less than {MEDIAN_HOUSE_VALUE_PERCENTILE}th "
f"percentile and is low income"
)
HOUSING_BURDEN_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for housing burden and is low income"
IMPENETRABLE_SURFACES_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for impenetrable surfaces and is low "
f"income"
)
# Remediation and Reduction of Legacy Pollution
RMP_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for proximity to RMP sites and is low income"
SUPERFUND_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for proximity to superfund sites and is low income"
HAZARDOUS_WASTE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for proximity to hazardous waste facilities and is low income"
AIR_TOXICS_CANCER_RISK_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for air toxics cancer risk and is low income"
RESPIRATORY_HAZARD_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for respiratory hazard index and is low income"
# Critical Clean Water and Waste Infrastructure
WASTEWATER_DISCHARGE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for wastewater discharge and is low income"
# Health Burden
# Health Burdens
DIABETES_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for diabetes and is low income"
)
@ -234,25 +277,35 @@ HEART_DISEASE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for
LIFE_EXPECTANCY_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for life expectancy and is low income"
HEALTHY_FOOD_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for low "
f"access to healthy food and is low income"
)
# Workforce
UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for unemployment"
" and low HS education"
" and has low HS education"
)
LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for households in linguistic isolation"
" and low HS education"
" and has low HS education"
)
POVERTY_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for households at or below 100% federal poverty level"
" and low HS education"
" and has low HS education"
)
LOW_READING_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for low 3rd grade reading proficiency"
" and has low HS education"
)
MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD = (
f"At or below the {PERCENTILE}th percentile for median income"
" and low HS education"
" and has low HS education"
)
THRESHOLD_COUNT = "Total threshold criteria exceeded"

View file

@ -54,7 +54,7 @@ class ScoreC(Score):
[
field_names.AIR_TOXICS_CANCER_RISK_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.RESPITORY_HAZARD_FIELD
field_names.RESPIRATORY_HAZARD_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,

View file

@ -36,7 +36,7 @@ class ScoreF(Score):
)
| (
self.df[
field_names.RESPITORY_HAZARD_FIELD
field_names.RESPIRATORY_HAZARD_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
> 0.9

View file

@ -177,6 +177,8 @@ class ScoreL(Score):
field_names.EXPECTED_POPULATION_LOSS_RATE_LOW_INCOME_FIELD,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_LOW_INCOME_FIELD,
field_names.EXPECTED_BUILDING_LOSS_RATE_LOW_INCOME_FIELD,
field_names.EXTREME_HEAT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD,
field_names.IMPENETRABLE_SURFACES_LOW_INCOME_FIELD,
]
expected_population_loss_threshold = (
@ -203,6 +205,28 @@ class ScoreL(Score):
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
extreme_heat_median_home_value_threshold = (
self.df[
field_names.EXTREME_HEAT_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
) & (
self.df[
field_names.MEDIAN_HOUSE_VALUE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
<= self.MEDIAN_HOUSE_VALUE_THRESHOLD
)
impenetrable_surfaces_threshold = (
self.df[
field_names.IMPENETRABLE_SURFACES_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
self.df[field_names.EXPECTED_POPULATION_LOSS_RATE_LOW_INCOME_FIELD] = (
expected_population_loss_threshold
& self.df[field_names.FPL_200_SERIES]
@ -218,6 +242,18 @@ class ScoreL(Score):
& self.df[field_names.FPL_200_SERIES]
)
self.df[
field_names.EXTREME_HEAT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD
] = (
extreme_heat_median_home_value_threshold
& self.df[field_names.FPL_200_SERIES]
)
self.df[field_names.IMPENETRABLE_SURFACES_LOW_INCOME_FIELD] = (
impenetrable_surfaces_threshold
& self.df[field_names.FPL_200_SERIES]
)
self._increment_total_eligibility_exceeded(climate_eligibility_columns)
return self.df[climate_eligibility_columns].any(axis="columns")
@ -320,11 +356,11 @@ class ScoreL(Score):
# poverty level. Source: Census's American Community Survey]
housing_eligibility_columns = [
field_names.LEAD_PAINT_MEDIAN_HOME_VALUE_LOW_INCOME_FIELD,
field_names.LEAD_PAINT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD,
field_names.HOUSING_BURDEN_LOW_INCOME_FIELD,
]
lead_paint_median_house_hold_threshold = (
lead_paint_median_home_value_threshold = (
self.df[
field_names.LEAD_PAINT_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
@ -347,8 +383,8 @@ class ScoreL(Score):
)
# series by series indicators
self.df[field_names.LEAD_PAINT_MEDIAN_HOME_VALUE_LOW_INCOME_FIELD] = (
lead_paint_median_house_hold_threshold
self.df[field_names.LEAD_PAINT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD] = (
lead_paint_median_home_value_threshold
& self.df[field_names.FPL_200_SERIES]
)
@ -371,6 +407,8 @@ class ScoreL(Score):
field_names.RMP_LOW_INCOME_FIELD,
field_names.SUPERFUND_LOW_INCOME_FIELD,
field_names.HAZARDOUS_WASTE_LOW_INCOME_FIELD,
field_names.AIR_TOXICS_CANCER_RISK_LOW_INCOME_FIELD,
field_names.RESPIRATORY_HAZARD_LOW_INCOME_FIELD,
]
rmp_sites_threshold = (
@ -390,6 +428,22 @@ class ScoreL(Score):
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
air_toxics_cancer_risk_threshold = (
self.df[
field_names.AIR_TOXICS_CANCER_RISK_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
respiratory_hazard_risk_threshold = (
self.df[
field_names.RESPIRATORY_HAZARD_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
# individual series-by-series
self.df[field_names.RMP_LOW_INCOME_FIELD] = (
rmp_sites_threshold & self.df[field_names.FPL_200_SERIES]
@ -400,6 +454,14 @@ class ScoreL(Score):
self.df[field_names.HAZARDOUS_WASTE_LOW_INCOME_FIELD] = (
tsdf_sites_threshold & self.df[field_names.FPL_200_SERIES]
)
self.df[field_names.AIR_TOXICS_CANCER_RISK_LOW_INCOME_FIELD] = (
air_toxics_cancer_risk_threshold
& self.df[field_names.FPL_200_SERIES]
)
self.df[field_names.RESPIRATORY_HAZARD_LOW_INCOME_FIELD] = (
respiratory_hazard_risk_threshold
& self.df[field_names.FPL_200_SERIES]
)
self._increment_total_eligibility_exceeded(
pollution_eligibility_columns
@ -449,6 +511,7 @@ class ScoreL(Score):
field_names.DIABETES_LOW_INCOME_FIELD,
field_names.ASTHMA_LOW_INCOME_FIELD,
field_names.HEART_DISEASE_LOW_INCOME_FIELD,
field_names.HEALTHY_FOOD_LOW_INCOME_FIELD,
field_names.LIFE_EXPECTANCY_LOW_INCOME_FIELD,
]
@ -474,6 +537,14 @@ class ScoreL(Score):
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
healthy_food_threshold = (
self.df[
field_names.HEALTHY_FOOD_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
life_expectancy_threshold = (
self.df[
field_names.LIFE_EXPECTANCY_FIELD
@ -496,6 +567,9 @@ class ScoreL(Score):
self.df[field_names.LIFE_EXPECTANCY_LOW_INCOME_FIELD] = (
life_expectancy_threshold & self.df[field_names.FPL_200_SERIES]
)
self.df[field_names.HEALTHY_FOOD_LOW_INCOME_FIELD] = (
healthy_food_threshold & self.df[field_names.FPL_200_SERIES]
)
self._increment_total_eligibility_exceeded(health_eligibility_columns)
@ -513,6 +587,15 @@ class ScoreL(Score):
# Where the high school degree achievement rates for adults 25 years and older is less than 95%
# (necessary to screen out university block groups)
# Workforce criteria for states fields.
workforce_eligibility_columns = [
field_names.UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD,
field_names.POVERTY_LOW_HS_EDUCATION_FIELD,
field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD,
field_names.MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
field_names.LOW_READING_LOW_HS_EDUCATION_FIELD,
]
high_scool_achievement_rate_threshold = (
self.df[field_names.HIGH_SCHOOL_ED_FIELD]
>= self.LACK_OF_HIGH_SCHOOL_MINIMUM_THRESHOLD
@ -528,7 +611,7 @@ class ScoreL(Score):
median_income_threshold = (
self.df[
field_names.MEDIAN_INCOME_PERCENT_AMI_FIELD
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
# Note: a high median income as a % of AMI is good, so take 1 minus the threshold to invert it.
@ -552,6 +635,14 @@ class ScoreL(Score):
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
low_reading_threshold = (
self.df[
field_names.LOW_READING_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
self.df[field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD] = (
linguistic_isolation_threshold
& high_scool_achievement_rate_threshold
@ -569,15 +660,9 @@ class ScoreL(Score):
unemployment_threshold & high_scool_achievement_rate_threshold
)
# Workforce criteria for states fields that create indicator columns
# for each tract in order to indicate whether they met any of the four
# criteria. We will used this create individual indicator columns.
workforce_eligibility_columns = [
field_names.UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD,
field_names.POVERTY_LOW_HS_EDUCATION_FIELD,
field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD,
field_names.MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
]
self.df[field_names.LOW_READING_LOW_HS_EDUCATION_FIELD] = (
low_reading_threshold & high_scool_achievement_rate_threshold
)
workforce_combined_criteria_for_states = self.df[
workforce_eligibility_columns