diff --git a/src/features/rap/Row.tsx b/src/features/rap/Row.tsx new file mode 100644 index 0000000..bf08c33 --- /dev/null +++ b/src/features/rap/Row.tsx @@ -0,0 +1,89 @@ +import styled from "@emotion/styled"; +import { WindsAloftAltitude, WindsAloftHour } from "../../models/WindsAloft"; +import Altitude from "./cells/Altitude"; +import Temperature from "./cells/Temperature"; +import WindDirection from "./cells/WindDirection"; +import WindSpeed from "./cells/WindSpeed"; +import { css } from "@emotion/react"; +import { vectorDifferenceMagnitude } from "../../helpers/vector"; + +const DELTA_WINDSPEED_VECTOR_THRESHOLD_KPH = 10; + +const TableRow = styled.tr<{ opaque: boolean }>` + ${({ opaque }) => + opaque && + css` + opacity: 0.5; + `} +`; + +interface RowProps { + datum: WindsAloftAltitude; + index: number; + displayedRapData: WindsAloftAltitude[]; + surfaceLevel: number; + windsAloftHour: WindsAloftHour; +} + +export default function Row({ + datum, + index, + displayedRapData, + surfaceLevel, + windsAloftHour, +}: RowProps) { + function negativeAltitude(datum: WindsAloftAltitude): boolean { + return !!(datum.altitudeInM - surfaceLevel < 0); + } + + const shearEligible = + displayedRapData[index - 1] && + vectorDifferenceMagnitude( + datum.windSpeedInKph, + datum.windDirectionInDeg, + displayedRapData[index - 1]?.windSpeedInKph, + displayedRapData[index - 1]?.windDirectionInDeg, + ) > DELTA_WINDSPEED_VECTOR_THRESHOLD_KPH; + + return ( + + + + + + + + + + + + + + + ); +} diff --git a/src/features/rap/Table.tsx b/src/features/rap/Table.tsx index a3cb4ea..a4336d8 100644 --- a/src/features/rap/Table.tsx +++ b/src/features/rap/Table.tsx @@ -1,10 +1,5 @@ -import { css } from "@emotion/react"; import styled from "@emotion/styled"; import { useAppDispatch, useAppSelector } from "../../hooks"; -import Altitude from "./cells/Altitude"; -import Temperature from "./cells/Temperature"; -import WindDirection from "./cells/WindDirection"; -import WindSpeed from "./cells/WindSpeed"; import { headerText } from "./CinCape"; import { WindsAloftAltitude, WindsAloftHour } from "../../models/WindsAloft"; import { @@ -22,6 +17,7 @@ import { toggleAltitude } from "../user/userSlice"; import { toggleAltitudeType } from "../../helpers/locale"; import { notEmpty } from "../../helpers/array"; import Tooltip from "../../shared/Tooltip"; +import Row from "./Row"; const TableEl = styled.table` width: 100%; @@ -35,14 +31,6 @@ const TableEl = styled.table` } `; -const Row = styled.tr<{ opaque: boolean }>` - ${({ opaque }) => - opaque && - css` - opacity: 0.5; - `} -`; - const InteractTh = styled.th` cursor: pointer; `; @@ -148,10 +136,6 @@ export default function Table({ surfaceLevel, ]); - function negativeAltitude(datum: WindsAloftAltitude): boolean { - return !!(datum.altitudeInM - surfaceLevel < 0); - } - return ( @@ -182,45 +166,14 @@ export default function Table({ {displayedRapData.map((datum, index) => ( - - - - - - - - - - - - - - + ))} diff --git a/src/features/rap/cells/WindDirection.tsx b/src/features/rap/cells/WindDirection.tsx index b0a4ae4..fe0dc1f 100644 --- a/src/features/rap/cells/WindDirection.tsx +++ b/src/features/rap/cells/WindDirection.tsx @@ -40,21 +40,27 @@ const Container = styled.div<{ shear: boolean }>` interface WindDirectionProps { curr: number; prev?: number; + shearEligible: boolean; } -export default function WindDirection({ curr, prev }: WindDirectionProps) { +export default function WindDirection({ + curr, + prev, + shearEligible, +}: WindDirectionProps) { const content = useMemo(() => { return ( - 25 + !!shearEligible && + !!prev && + Math.abs(getAngleDifference(curr, prev)) > 25 } > {Math.round(curr)} ); - }, [curr, prev]); + }, [curr, prev, shearEligible]); return content; } diff --git a/src/helpers/vector.ts b/src/helpers/vector.ts new file mode 100644 index 0000000..11feac3 --- /dev/null +++ b/src/helpers/vector.ts @@ -0,0 +1,23 @@ +export function vectorDifferenceMagnitude( + speed1: number, + direction1: number, + speed2: number, + direction2: number, +): number { + // Convert directions from degrees to radians + const radian1 = (Math.PI / 180) * direction1; + const radian2 = (Math.PI / 180) * direction2; + + // Convert polar coordinates to Cartesian coordinates + const x1 = speed1 * Math.cos(radian1); + const y1 = speed1 * Math.sin(radian1); + const x2 = speed2 * Math.cos(radian2); + const y2 = speed2 * Math.sin(radian2); + + // Calculate the difference in Cartesian coordinates + const dx = x2 - x1; + const dy = y2 - y1; + + // Calculate the magnitude of the difference vector + return Math.sqrt(dx * dx + dy * dy); +}