Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adding a speed segment map to speed page #1045

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions common/components/charts/Legend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
);
};

const LegendSingle: React.FC<LegendProps> = ({ showUnderRatio = false }) => {

Check warning on line 34 in common/components/charts/Legend.tsx

View workflow job for this annotation

GitHub Actions / frontend (20, 3.12)

'showUnderRatio' is assigned a value but never used
return (
<>
<div className="col-span-2 flex flex-row items-baseline gap-2 pb-1 italic lg:pb-0">
Expand Down Expand Up @@ -78,3 +78,47 @@
</div>
);
};

export const LegendSpeedMap: React.FC = () => {
return (
<Disclosure>
{({ open }) => (
<div className="flex w-full flex-col rounded-md border border-stone-100 text-stone-700 shadow-sm">
<Disclosure.Button className="">
<div className="flex flex-row items-center justify-between px-4 py-1">
<div className="flex w-full flex-row items-baseline gap-2 p-1 text-left text-xs sm:gap-4">
<p>
<span className="mr-1 inline-block h-2.5 w-5 rounded-full border border-[#57945B]/35 bg-[#64b96a]/50"></span>
{'Over 30 MPH'}
</p>
<p>
<span className="mr-1 inline-block h-2.5 w-5 rounded-full border border-[#D9D31E]/35 bg-[#f5ed00]/50"></span>
{'20-30 mph'}
</p>
<p>
<span
className={`mr-1 inline-block h-2.5 w-5 rounded-full border border-[#A1384A]/35 bg-[#c33149]/50`}
></span>{' '}
{'Under 20mph'}
</p>
</div>
<FontAwesomeIcon icon={open ? faChevronUp : faChevronDown} className="" />
</div>
</Disclosure.Button>
<Disclosure.Panel
className={
'grid w-full grid-cols-2 items-baseline p-1 px-4 text-left text-xs lg:flex lg:flex-row lg:gap-4'
}
>
Speed is calculated using the daily median of the travel times between the two stops,
divided by the distance between the two stops. These station distances are calculated
using official distances from a MBTA dataset. These distances may be different than the
distances between signals, and exclude dwells, leading to some higher or lower MPH
numbers than reality. These numbers are our best approximation of the speeds of trains
on the line.
</Disclosure.Panel>
</div>
)}
</Disclosure>
);
};
16 changes: 16 additions & 0 deletions common/types/map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { SegmentLocation } from '@transitmatters/stripmap';

export type SegmentDirection = '0' | '1';

export type ByDirection<T> = Record<SegmentDirection, T>;

export type WithSegmentLocation<T> = {
segmentLocation: SegmentLocation;
direction: SegmentDirection;
value: T;
};

export type SegmentationResult<T> = {
segments: T[];
effectiveDate: Date;
};
58 changes: 58 additions & 0 deletions common/utils/mapSegments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { SegmentLocation } from '@transitmatters/stripmap';
import type { LineShort } from '../types/lines';
import type { ByDirection, SegmentDirection, WithSegmentLocation } from '../types/map';
import type { Station } from '../types/stations';

export const DIRECTIONS = ['1', '0'] as const;

export const locateIntoSegments = <T extends Record<string, unknown>>(
records: T[],
getFromStation: (t: T) => null | Station,
getToStation: (t: T) => null | Station
) => {
const located: WithSegmentLocation<T>[] = [];
for (const record of records) {
const fromStation = getFromStation(record);
const toStation = getToStation(record);
if (fromStation && toStation) {
const fromComesFirst = fromStation.order < toStation.order;
const direction: SegmentDirection = fromComesFirst ? '0' : '1';
located.push({
direction,
segmentLocation: {
fromStationId: fromStation.station,
toStationId: toStation.station,
},
value: record,
});
}
}
return located;
};

export const filterActiveElements = <T extends Record<string, unknown>>(
records: T[],
targetLine: LineShort,
targetDate: Date,
getRecordDateRange: (t: T) => [Date, Date],
getRecordLine: (t: T) => LineShort
) => {
Comment on lines +33 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Quality Violation

Too many parameters (5). Maximum allowed is 4. (...read more)

Having too many parameters can make your code hard to read. The parameters must be used in appropriate order. Forgetting the order of parameters can cause mistakes.

Too many parameters is a code smell. You should refactor your code in smaller reusable bits. While it may be valid to require more than four parameters, you should use object destructuring.

View in Datadog  Leave us feedback  Documentation

return records.filter((record) => {
const [serviceDate, targetDate] = getRecordDateRange(record);
return serviceDate.valueOf() === targetDate.valueOf() && getRecordLine(record) === targetLine;
});
};

export const indexByDirection = <T>(records: WithSegmentLocation<T>[]): ByDirection<T[]> => {
return {
'0': records.filter((r) => r.direction === '0').map((r) => r.value),
'1': records.filter((r) => r.direction === '1').map((r) => r.value),
};
};

export const matchSegmentLocations = (first: SegmentLocation, second: SegmentLocation) => {
return (
(first.fromStationId === second.fromStationId && first.toStationId === second.toStationId) ||
(first.toStationId === second.fromStationId && first.fromStationId === second.toStationId)
);
};
5 changes: 4 additions & 1 deletion modules/landing/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ export const convertToSpeedDataset = (
});
};

function convertSecondsToMph(travelTimeSec: number | undefined, distanceMiles: number | undefined) {
export function convertSecondsToMph(
travelTimeSec: number | undefined,
distanceMiles: number | undefined
) {
if (distanceMiles && travelTimeSec) {
return (3600 * distanceMiles) / travelTimeSec;
}
Expand Down
23 changes: 12 additions & 11 deletions modules/slowzones/SlowZonesWidgetTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
import React from 'react';
import classNames from 'classnames';
import type { Line } from '../../common/types/lines';
import { useBreakpoint } from '../../common/hooks/useBreakpoint';
import { useDelimitatedRoute } from '../../common/utils/router';
import { formatDateTodayCheck } from '../../common/state/utils/dateStoreUtils';

interface SlowZonesWidgetTitle {
line?: Line;
subtitle?: string;
}

export const SlowZonesWidgetTitle: React.FC<SlowZonesWidgetTitle> = () => {
export const SlowZonesWidgetTitle: React.FC<SlowZonesWidgetTitle> = ({ subtitle }) => {
const isMobile = !useBreakpoint('md');
const { query } = useDelimitatedRoute();
const date = query.endDate ? formatDateTodayCheck(query.endDate) : undefined;

return (
<div className="flex w-full flex-col items-baseline justify-between gap-x-4 gap-y-1 pb-1 text-base md:flex-row md:text-xl">
<div className="flex w-full flex-col md:w-auto">
<div className="flex w-full flex-row items-baseline justify-between">
<h2
className={classNames('font-semibold', 'whitespace-nowrap leading-tight text-stone-800')}
>
Line map
</h2>
{isMobile && <Date date={date} />}
{subtitle && (
<h2
className={classNames(
'font-semibold',
'whitespace-nowrap leading-tight text-stone-800'
)}
className={classNames('whitespace-nowrap text-sm italic leading-tight text-stone-600')}
>
Line map
{subtitle}
</h2>
{isMobile && <Date date={date} />}
</div>
)}
</div>
<div className="flex w-full flex-shrink flex-col overflow-hidden md:items-end">
{!isMobile && <Date date={date} />}
Expand Down
5 changes: 2 additions & 3 deletions modules/slowzones/map/DirectionIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from 'react';

import type { SlowZoneDirection } from './segment';

import type { SegmentDirection } from '../../../common/types/map';
import styles from './DirectionIndicator.module.css';

interface DirectionIndicatorProps {
direction: SlowZoneDirection;
direction: SegmentDirection;
color: string;
isHorizontal: boolean;
size?: number;
Expand Down
2 changes: 1 addition & 1 deletion modules/slowzones/map/SlowSegmentLabel.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
display: flex;
align-items: baseline;
gap: 1.5px;
}
}
9 changes: 5 additions & 4 deletions modules/slowzones/map/SlowSegmentLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import type { LineMetadata } from '../../../common/types/lines';
import { getClockFormattedTimeString } from '../../../common/utils/time';
import { useDelimitatedRoute } from '../../../common/utils/router';
import { TODAY_STRING } from '../../../common/constants/dates';
import type { ByDirection, SlowZoneDirection, SlowZonesSegment } from './segment';
import { DIRECTIONS } from './segment';
import type { ByDirection, SegmentDirection } from '../../../common/types/map';
import { DIRECTIONS } from '../../../common/utils/mapSegments';
import type { SlowZonesSegment } from './segment';

import styles from './SlowSegmentLabel.module.css';

interface SlowZoneLabelProps {
direction: SlowZoneDirection;
direction: SegmentDirection;
slowZone: SlowZoneResponse;
color: string;
offset: number;
Expand Down Expand Up @@ -129,7 +130,7 @@ export const SlowSegmentLabel: React.FC<SlowSegmentLabelProps> = (props) => {
return (
<SlowZoneLabel
key={direction}
direction={direction as SlowZoneDirection}
direction={direction as SegmentDirection}
slowZone={zone}
color={line.color}
isHorizontal={isHorizontal}
Expand Down
25 changes: 2 additions & 23 deletions modules/slowzones/map/SlowZonesMap.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useMemo } from 'react';
import type { SegmentLocation, SegmentLabel, TooltipSide } from '@transitmatters/stripmap';
import type { SegmentLocation, TooltipSide } from '@transitmatters/stripmap';
import { LineMap, createDefaultDiagramForLine } from '@transitmatters/stripmap';

import '@transitmatters/stripmap/dist/style.css';
Expand All @@ -11,7 +11,7 @@ import type { SlowZonesLineName } from '../types';
import { getSlowZoneOpacity } from '../../../common/utils/slowZoneUtils';
import { useDelimitatedRoute } from '../../../common/utils/router';
import { TODAY_STRING } from '../../../common/constants/dates';
import { segmentSlowZones } from './segment';
import { getSegmentLabelOverrides, segmentSlowZones } from './segment';
import { SlowSegmentLabel } from './SlowSegmentLabel';
import { SlowZonesTooltip } from './SlowZonesTooltip';

Expand All @@ -36,27 +36,6 @@ const abbreviateStationName = ({ stationName }) => {
.replace('North Quincy', 'N. Quincy');
};

const getSegmentLabelOverrides = (
segmentLocation: SegmentLocation,
isHorizontal: boolean
): null | Partial<SegmentLabel> => {
const { toStationId } = segmentLocation;
// JFK to Savin Hill — on a steep curve
if (toStationId === 'place-shmnl') {
return {
mapSide: '1' as const,
offset: isHorizontal ? { x: -12, y: -5 } : { x: 0, y: 0 },
};
}
// Shawmut to Ashmont — obscured by "North Quincy" label
if (!isHorizontal && toStationId === 'place-asmnl') {
return {
mapSide: '1' as const,
};
}
return null;
};

export const SlowZonesMap: React.FC<SlowZonesMapProps> = ({
lineName,
slowZones,
Expand Down
2 changes: 1 addition & 1 deletion modules/slowzones/map/SlowZonesTooltip.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@
.speedRestrictionStats {
display: flex;
flex-direction: column;
}
}
7 changes: 4 additions & 3 deletions modules/slowzones/map/SlowZonesTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { prettyDate } from '../../../common/utils/date';

import { TODAY_STRING } from '../../../common/constants/dates';
import { useDelimitatedRoute } from '../../../common/utils/router';
import { DIRECTIONS } from './segment';
import type { ByDirection, SlowZoneDirection, SlowZonesSegment } from './segment';
import type { ByDirection, SegmentDirection } from '../../../common/types/map';
import { DIRECTIONS } from '../../../common/utils/mapSegments';
import type { SlowZonesSegment } from './segment';
import { DirectionIndicator } from './DirectionIndicator';

import styles from './SlowZonesTooltip.module.css';
Expand Down Expand Up @@ -98,7 +99,7 @@ export const SlowZonesTooltip: React.FC<SlowZonesTooltipProps> = (props) => {
return null;
};

const renderSlowZoneForDirection = (direction: SlowZoneDirection) => {
const renderSlowZoneForDirection = (direction: SegmentDirection) => {
const isToday = endDate === TODAY_STRING;

const [slowZone] = slowZones[direction];
Expand Down
Loading
Loading