diff --git a/app/tract/[tract]/page.tsx b/app/tract/[tract]/page.tsx index eb813a2..9867409 100644 --- a/app/tract/[tract]/page.tsx +++ b/app/tract/[tract]/page.tsx @@ -146,7 +146,7 @@ const TractPage: React.FC = async ({ params }) => { 100 - +data[segregationTemplate.column as keyof typeof data], +data[economicAdvantageTemplate.column as keyof typeof data] ] - console.log('food access', foodAccesstemplate) + console.log('food access', data) const foodAccessText = getThresholdValue(foodAccess, data, foodAccesstemplate) const marketPowerText = getThresholdValue(marketPower, data, marketPowerTemplate) const segregationText = getThresholdValue(segregation, data, segregationTemplate) diff --git a/components/LegendBreakText/LegendBreakText.tsx b/components/LegendBreakText/LegendBreakText.tsx index 560d35d..02eaff7 100644 --- a/components/LegendBreakText/LegendBreakText.tsx +++ b/components/LegendBreakText/LegendBreakText.tsx @@ -1,15 +1,20 @@ import { formatNumber } from "utils/display/formatNumber" import { LegendBreakTextProps } from "./types" import { deepCompare2d1d } from "utils/data/compareArrayElements" +import { getDecimalsFromRange } from "utils/data/service/service" export const LegendBreakText: React.FC = ({ breaks, index, colors, onClick, colorFilter }) => { + // @ts-ignore + const range = breaks[0] - breaks[breaks.length - 1] + const numFractionDigits = getDecimalsFromRange(Math.abs(range)) + let text = "" if (index === 0) { - text = `< ${formatNumber(breaks[0] as number)}` + text = `< ${formatNumber(breaks[0] as number, numFractionDigits)}` } else if (index === breaks.length) { - text = `> ${formatNumber(breaks[breaks.length - 1] as number)}` + text = `> ${formatNumber(breaks[breaks.length - 1] as number, numFractionDigits)}` } else { - text = `${formatNumber(breaks[index - 1] as number)} - ${formatNumber(breaks[index] as number)}` + text = `${formatNumber(breaks[index - 1] as number, numFractionDigits)} - ${formatNumber(breaks[index] as number, numFractionDigits)}` } const color = colors[index]! const isOpaque = !colorFilter?.length || deepCompare2d1d(colorFilter, color) diff --git a/utils/data/config.ts b/utils/data/config.ts index 9c978da..d5ef8d7 100644 --- a/utils/data/config.ts +++ b/utils/data/config.ts @@ -65,6 +65,7 @@ export const columnsDict = { column: "TOTAL_POPULATION", description: "Total population (ACS 2021 5-year estimates)", bivariate: false, + nbins: 5, colorScheme: "schemeBlues", }, "Median Household Income": { @@ -77,6 +78,7 @@ export const columnsDict = { name: "Poverty Rate", column: "POVERTY_RATE", description: "Poverty rate", + colorScheme: "schemeBuPu", bivariate: false, }, "No Healthcare (Percent)": { @@ -87,10 +89,52 @@ export const columnsDict = { }, "Percent Black or African American": { name: "Percent Black or African American", - column: "NH BLACK ALONE", + column: '"PCT NH BLACK"', description: "Percentage of population that is Black or African American", bivariate: false, }, + "Percent Hispanic or Latinx": { + name: "Percent Hispanic or Latinx", + column: '"PCT HISPANIC OR LATINO"', + description: "Percentage of population that is Hispanic or Latinx", + bivariate: false, + }, + "Percent American Indian": { + name: "Percent American Indian or Native American", + column: '"PCT NH AMERICAN INDIAN"', + description: "Percentage of population that is American Indian or Native American", + bivariate: false, + }, + "Percent Asian": { + name: "Percent Asian", + column: '"PCT NH ASIAN"', + description: "Percentage of population that is Asian", + bivariate: false, + }, + "Percent Pacific Islander": { + name: "Percent Pacific Islander or Native Hawaiian", + column: '"PCT NH PACIFIC ISLANDER"', + description: "Percentage of population that is Pacific Islander or Native Hawaiian", + bivariate: false, + }, + "Percent Other": { + name: "Percent Other", + column: '"PCT NH other"', + description: "Percentage of population that is Other", + bivariate: false, + }, + "Percent Two or More": { + name: "Percent Two or More", + column: '"PCT NH TWO OR MORE"', + description: "Percentage of population that is Two or More", + bivariate: false, + }, + "Percent White": { + name: "Percent White", + column: '"PCT NH WHITE"', + description: "Percentage of population that is White", + bivariate: false, + }, "Concentration & Food Access - Bivariate 2020": { name: "Concentration & Food Access - 2020", bivariate: true, @@ -179,6 +223,13 @@ export const columnGroups: ColumnGroups = { "Poverty Rate", "No Healthcare (Percent)", "Percent Black or African American", + "Percent Hispanic or Latinx", + "Percent American Indian", + "Percent Asian", + "Percent Pacific Islander", + "Percent Other", + "Percent Two or More", + "Percent White", ], }, Bivariate: { diff --git a/utils/data/service/service.ts b/utils/data/service/service.ts index e66bd4b..e8d50dc 100644 --- a/utils/data/service/service.ts +++ b/utils/data/service/service.ts @@ -6,6 +6,22 @@ import { BivariateColorParamteres, MonovariateColorParamteres, d3Bivariate } fro import { deepCompare2d1d } from "../compareArrayElements" import type { AsyncDuckDBConnection } from "@duckdb/duckdb-wasm" export const dataTableName = "data.parquet" + +export const getDecimalsFromRange = (range: number) => { + if (range < 0.01) { + return 12 + } else if (range < 0.1) { + return 8 + } else if (range < 1) { + return 5 + } else if (range < 10) { + return 2 + } else if (range < 100) { + return 1 + } else { + return 0 + } +} export class DataService> { data: Record>> = {} dbStatus: "none" | "loading" | "loaded" | "error" = "none" @@ -57,8 +73,10 @@ export class DataService> { // eg. n=5 - 0.2, 0.4, 0.6, 0.8 - 4 breaks // eg. n=4 - 0.25, 0.5, 0.75 - 3 breaks const quantileFractions = Array.from({ length: n - 1 }, (_, i) => (i + 1) / n) + const rangeResponse = await this.runQuery(`SELECT MAX(${column}) - MIN(${column}) as range FROM ${dataTableName}`) + const sigFigs = getDecimalsFromRange(rangeResponse[0].range) let query = `SELECT - ${quantileFractions.map((f, i) => `round(approx_quantile(${column}, ${f}), 3) as break${i}`)} + ${quantileFractions.map((f, i) => `round(approx_quantile(${column}, ${f}), ${sigFigs}) as break${i}`)} FROM ${dataTableName} ` if (filter) { @@ -165,10 +183,12 @@ export class DataService> { async getMonovariateColorValues({ colorScheme, column, nBins, reversed, filter, range }: MonovariateColorParamteres) { const cleanColumn = typeof column === "string" ? column : `"${column}"` + const n = nBins || 5 + const cleanColorScheme = colorScheme || "schemeYlGn" // @ts-ignore - const d3Colors = d3[colorScheme]?.[nBins] + const d3Colors = d3[cleanColorScheme]?.[n] if (!d3Colors) { - console.error(`Color scheme ${colorScheme} with ${nBins} bins not found`) + console.error(`Color scheme ${cleanColorScheme} with ${n} bins not found`) return { colorMap: {}, breaks: [], @@ -185,7 +205,7 @@ export class DataService> { const values = range === "categorical" ? await this.getUniqueValues(cleanColumn, filter) - : await this.getQuantiles(cleanColumn, nBins, filter) + : await this.getQuantiles(cleanColumn, n, filter) let query = `` if (!range || range === "continuous") { diff --git a/utils/display/formatNumber.ts b/utils/display/formatNumber.ts index 37aa1bc..d5b438a 100644 --- a/utils/display/formatNumber.ts +++ b/utils/display/formatNumber.ts @@ -1,9 +1,9 @@ -export const formatNumber = (n: number) => { +export const formatNumber = (n: number, fractionDigits = 2) => { if (typeof n == "number") { const formatter = new Intl.NumberFormat("en-US", { style: "decimal", notation: "compact", - maximumFractionDigits: 2, + maximumFractionDigits: fractionDigits, }) return formatter.format(n) } else {