Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into commuter-rail-in-dd
Browse files Browse the repository at this point in the history
  • Loading branch information
devinmatte committed Jan 17, 2024
2 parents 8db9c46 + 52d9d95 commit a094889
Show file tree
Hide file tree
Showing 40 changed files with 8,288 additions and 729 deletions.
56 changes: 15 additions & 41 deletions common/components/buttons/DownloadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,26 @@ import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFileArrowDown } from '@fortawesome/free-solid-svg-icons';
import classNames from 'classnames';
import type { DataPoint } from '../../types/dataPoints';
import type { AggregateDataPoint, Location } from '../../types/charts';
import type { Location } from '../../types/charts';
import { lineColorTextHover } from '../../styles/general';
import { useDelimitatedRoute } from '../../utils/router';

const directionAbbrs = {
northbound: 'NB',
southbound: 'SB',
eastbound: 'EB',
westbound: 'WB',
inbound: 'IB',
outbound: 'OB',
};

function filename(
datasetName: string,
location: Location,
bothStops: boolean,
startDate: string,
endDate?: string
) {
// CharlesMGH-SB_dwells_20210315.csv
// CentralSquareCambridge-MelneaCassWashington_traveltimesByHour-weekday_20200101-20201231.csv
// BostonUniversityWest-EB_headways_20161226-20170328.csv
const fromStop = location.from.replace(/[^A-z]/g, '');
const toStop = location.to.replace(/[^A-z]/g, '');
const dir = directionAbbrs[location.direction];
const where = `${fromStop}-${bothStops ? toStop : dir}`;

const what = datasetName;

const date1 = startDate.replaceAll('-', '');
const date2 = endDate ? `-${endDate.replaceAll('-', '')}` : '';
const when = `${date1}${date2}`;

return `${where}_${what}_${when}.csv`;
}
import { getCsvFilename } from '../../utils/csv';

interface DownloadButtonProps {
datasetName: string;
data: (DataPoint | AggregateDataPoint)[];
location: Location;
bothStops: boolean;
data: Record<string, any>[];
startDate: string;
includeBothStopsForLocation?: boolean;
location?: Location;
endDate?: string;
}

export const DownloadButton: React.FC<DownloadButtonProps> = ({
datasetName,
data,
location,
bothStops,
includeBothStopsForLocation,
startDate,
location,
endDate,
}) => {
const { line } = useDelimitatedRoute();
Expand All @@ -65,7 +32,14 @@ export const DownloadButton: React.FC<DownloadButtonProps> = ({
className={'csv-link'}
data={data}
title={'Download data as CSV'}
filename={filename(datasetName, location, bothStops, startDate, endDate)}
filename={getCsvFilename({
datasetName,
includeBothStopsForLocation,
startDate,
line,
location,
endDate,
})}
>
<FontAwesomeIcon
icon={faFileArrowDown}
Expand Down
24 changes: 14 additions & 10 deletions common/components/charts/AggregateLineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
data,
location,
pointField,
bothStops = false,
includeBothStopsForLocation = false,
fname,
timeUnit,
timeFormat,
Expand All @@ -44,12 +44,15 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
suggestedYMax,
showLegend = true,
byTime = false,
yUnit = 'Minutes',
}) => {
const ref = useRef();
const hourly = timeUnit === 'hour';
const isMobile = !useBreakpoint('md');
const labels = useMemo(() => data.map((item) => item[pointField]), [data, pointField]);

const multiplier = yUnit === 'Minutes' ? 1 / 60 : 1;

return (
<ChartBorder>
<ChartDiv isMobile={isMobile}>
Expand All @@ -72,7 +75,7 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
pointRadius: byTime ? 0 : 3,
pointHitRadius: 10,
stepped: byTime,
data: data.map((item: AggregateDataPoint) => (item['50%'] / 60).toFixed(2)),
data: data.map((item: AggregateDataPoint) => (item['50%'] * multiplier).toFixed(2)),
},
{
label: '25th percentile',
Expand All @@ -81,7 +84,7 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
stepped: byTime,
tension: 0.4,
pointRadius: 0,
data: data.map((item: AggregateDataPoint) => (item['25%'] / 60).toFixed(2)),
data: data.map((item: AggregateDataPoint) => (item['25%'] * multiplier).toFixed(2)),
},
{
label: '75th percentile',
Expand All @@ -90,7 +93,7 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
stepped: byTime,
tension: 0.4,
pointRadius: 0,
data: data.map((item: AggregateDataPoint) => (item['75%'] / 60).toFixed(2)),
data: data.map((item: AggregateDataPoint) => (item['75%'] * multiplier).toFixed(2)),
},
],
}}
Expand All @@ -99,7 +102,7 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
y: {
title: {
display: true,
text: 'Minutes',
text: yUnit,
},
ticks: {
precision: 1,
Expand Down Expand Up @@ -146,10 +149,11 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
position: 'nearest',
callbacks: {
label: (tooltipItem) => {
return `${tooltipItem.dataset.label}: ${getFormattedTimeString(
tooltipItem.parsed.y,
'minutes'
)}`;
return `${tooltipItem.dataset.label}: ${
yUnit === 'Minutes'
? getFormattedTimeString(tooltipItem.parsed.y, 'minutes')
: `${tooltipItem.parsed.y} ${yUnit}`
}`;
},
},
},
Expand All @@ -175,7 +179,7 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
data={data}
datasetName={fname}
location={location}
bothStops={bothStops}
includeBothStopsForLocation={includeBothStopsForLocation}
startDate={startDate}
/>
)}
Expand Down
12 changes: 11 additions & 1 deletion common/components/charts/SingleChartWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import { ChartPlaceHolder } from '../graphics/ChartPlaceHolder';
import { TravelTimesSingleChart } from '../../../modules/traveltimes/charts/TravelTimesSingleChart';
import { HeadwaysSingleChart } from '../../../modules/headways/charts/HeadwaysSingleChart';
import { DwellsSingleChart } from '../../../modules/dwells/charts/DwellsSingleChart';
import { SpeedBetweenStationsSingleChart } from '../../../modules/speed/charts/SpeedBetweenStationsSingleChart';

interface SingleChartWrapperProps {
query: UseQueryResult<SingleDayDataPoint[]>;
toStation: Station | undefined;
fromStation: Station | undefined;
type: 'headways' | 'traveltimes' | 'dwells';
type: 'headways' | 'traveltimes' | 'dwells' | 'speeds';
showLegend?: boolean;
}

Expand Down Expand Up @@ -47,5 +48,14 @@ export const SingleChartWrapper: React.FC<SingleChartWrapperProps> = ({
return (
<DwellsSingleChart dwells={query.data} fromStation={fromStation} toStation={toStation} />
);
case 'speeds':
return (
<SpeedBetweenStationsSingleChart
traveltimes={query.data}
fromStation={fromStation}
toStation={toStation}
showLegend={showLegend}
/>
);
}
};
25 changes: 16 additions & 9 deletions common/components/charts/SingleDayLineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ export const SingleDayLineChart: React.FC<SingleDayLineProps> = ({
pointField,
benchmarkField,
fname,
bothStops = false,
includeBothStopsForLocation = false,
location,
units,
showLegend = true,
}) => {
const ref = useRef();
Expand All @@ -79,10 +80,15 @@ export const SingleDayLineChart: React.FC<SingleDayLineProps> = ({
);
const displayBenchmarkData = benchmarkData.every((datapoint) => datapoint !== undefined);
// Have to use `as number` because typescript doesn't understand `datapoint` is not undefined.
const multiplier = units === 'Minutes' ? 1 / 60 : 1;
const benchmarkDataFormatted = displayBenchmarkData
? benchmarkData.map((datapoint) => ((datapoint as number) / 60).toFixed(2))
? benchmarkData.map((datapoint) => ((datapoint as number) * multiplier).toFixed(2))
: null;

const convertedData = data.map((datapoint) =>
((datapoint[metricField] as number) * multiplier).toFixed(2)
);

return (
<ChartBorder>
<ChartDiv isMobile={isMobile}>
Expand All @@ -103,7 +109,7 @@ export const SingleDayLineChart: React.FC<SingleDayLineProps> = ({
pointHoverBackgroundColor: pointColors(data, metricField, benchmarkField),
pointRadius: 3,
pointHitRadius: 10,
data: data.map((datapoint) => ((datapoint[metricField] as number) / 60).toFixed(2)),
data: convertedData,
},
{
label: `Benchmark MBTA`,
Expand Down Expand Up @@ -133,10 +139,11 @@ export const SingleDayLineChart: React.FC<SingleDayLineProps> = ({
) {
return '';
}
return `${tooltipItem.dataset.label}: ${getFormattedTimeString(
tooltipItem.parsed.y,
'minutes'
)}`;
return `${tooltipItem.dataset.label}: ${
units === 'Minutes'
? getFormattedTimeString(tooltipItem.parsed.y, 'minutes')
: `${tooltipItem.parsed.y} ${units}`
}`;
},
afterBody: (tooltipItems) => {
return departureFromNormalString(
Expand All @@ -162,7 +169,7 @@ export const SingleDayLineChart: React.FC<SingleDayLineProps> = ({
},
title: {
display: true,
text: 'Minutes',
text: units,
color: COLORS.design.subtitleGrey,
},
},
Expand Down Expand Up @@ -222,7 +229,7 @@ export const SingleDayLineChart: React.FC<SingleDayLineProps> = ({
data={data}
datasetName={fname}
location={location}
bothStops={bothStops}
includeBothStopsForLocation={includeBothStopsForLocation}
startDate={date}
/>
)}
Expand Down
7 changes: 1 addition & 6 deletions common/components/charts/TimeSeriesChart/TimeSeriesChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type { ChartData } from 'chart.js';

import { enUS } from 'date-fns/locale';
import { useBreakpoint } from '../../../hooks/useBreakpoint';
import { ChartBorder } from '../ChartBorder';
import { ChartDiv } from '../ChartDiv';
import { CHART_COLORS, COLORS } from '../../../constants/colors';

Expand Down Expand Up @@ -282,9 +281,5 @@ export const TimeSeriesChart = <Data extends Dataset[]>(props: Props<Data>) => {
);
}, [isMobile, chartJsData, chartJsOptions, chartJsPlugins]);

return (
<ChartBorder>
<ChartDiv isMobile={isMobile}>{chart}</ChartDiv>
</ChartBorder>
);
return <ChartDiv isMobile={isMobile}>{chart}</ChartDiv>;
};
2 changes: 1 addition & 1 deletion common/constants/dates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const THREE_MONTHS_AGO_STRING = TODAY.subtract(90, 'days').format(DATE_FO
const OVERVIEW_TRAIN_MIN_DATE = '2016-02-01';
const TRAIN_MIN_DATE = '2016-01-15';
const BUS_MIN_DATE = '2018-08-01';
export const BUS_MAX_DATE = '2023-11-30';
export const BUS_MAX_DATE = '2023-12-31';
const BUS_MAX_DAY = dayjs(BUS_MAX_DATE);
export const BUS_MAX_DATE_MINUS_ONE_WEEK = dayjs(BUS_MAX_DATE)
.subtract(7, 'days')
Expand Down
Loading

0 comments on commit a094889

Please sign in to comment.