Heading
The heading and course over ground on a single card, with a heading setpoint and advice arcs.
On this page
Code
import {
AdviceType,
CogArrowStyle,
CompassReadoutSource,
HdgArrowStyle,
Heading,
HeadingPriorityElement,
Priority,
VesselImage,
type AngleAdvice,
type CompassCenterReadout,
} from "@bosunkit/instruments";
import { useEffect, useState, type CSSProperties, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"Hand steering up a buoyed channel on a live feed: the heading swings around the setpoint and into the caution " +
"sector over the bank, and the setpoint is adjusted and confirmed every eight seconds, on three cards with " +
"different arrows, centers and palettes.";
const CENTER_READOUTS: CompassCenterReadout[] = [
{ source: CompassReadoutSource.hdg },
{ source: CompassReadoutSource.cog },
];
/** The leading line through the channel, and the bank on its starboard side. */
const ADVICES: AngleAdvice[] = [
{ minAngle: 20, maxAngle: 34, type: AdviceType.advice, hinted: true },
{ minAngle: 40, maxAngle: 62, type: AdviceType.caution, hinted: true },
];
const PANEL: CSSProperties = {
display: "flex",
flexWrap: "wrap",
alignItems: "flex-end",
justifyContent: "center",
gap: 28,
width: "100%",
maxWidth: 680,
};
const FIGURE: CSSProperties = { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, margin: 0 };
const CAPTION: CSSProperties = { fontSize: 13, color: "var(--text-dim)", textAlign: "center", maxWidth: 220 };
/** The heading the feed reports at a given second: a slow swing with a faster yaw on top. */
function headingAt(second: number): number {
return 30 + 10 * Math.sin(second / 18) + 2 * Math.sin(second / 3);
}
export default function Showcase(): ReactElement {
const [second, setSecond]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0);
useEffect((): (() => void) => {
const interval: number = window.setInterval((): void => {
setSecond((previous: number): number => previous + 1);
}, 1000);
return (): void => {
window.clearInterval(interval);
};
}, []);
const heading: number = (headingAt(second) + 360) % 360;
// The flood sets the vessel to port of her heading.
const courseOverGround: number = (heading - 6 + 360) % 360;
// The setpoint alternates between 27 and 33 degrees. In the two seconds before each change the helmsman is turning
// the knob, so the new value shows as a second marker; on release the marker slides across.
const phase: number = second % 8;
const confirmed: number = Math.floor(second / 8) % 2 === 0 ? 27 : 33;
const next: number = confirmed === 27 ? 33 : 27;
const adjusting: boolean = phase >= 6;
return (
<div style={PANEL}>
<figure style={FIGURE}>
<Heading
heading={heading}
courseOverGround={courseOverGround}
headingSetpoint={confirmed}
newHeadingSetpoint={adjusting ? next : undefined}
touching={adjusting}
autoAtHeadingSetpoint
autoAtHeadingSetpointDeadband={3}
animateSetpoint
headingAdvices={ADVICES}
centerReadouts={CENTER_READOUTS}
hdgArrowStyle={HdgArrowStyle.arrowHead}
cogArrowStyle={CogArrowStyle.vector}
priority={Priority.enhanced}
priorityElements={[HeadingPriorityElement.hdg, HeadingPriorityElement.cog]}
showLabels
faceDiameter={250}
/>
<figcaption style={CAPTION}>Helm: readouts, the enhanced palette, advice and caution sectors</figcaption>
</figure>
<figure style={FIGURE}>
<Heading
heading={heading}
courseOverGround={courseOverGround}
headingSetpoint={confirmed}
newHeadingSetpoint={adjusting ? next : undefined}
touching={adjusting}
autoAtHeadingSetpoint
animateSetpoint
headingAdvices={ADVICES}
vesselImage={VesselImage.genericTop}
hdgArrowStyle={HdgArrowStyle.needle}
cogArrowStyle={CogArrowStyle.needle}
showLabels
tickmarksInside
faceDiameter={170}
/>
<figcaption style={CAPTION}>Cockpit repeater: the vessel, needles and inside tickmarks</figcaption>
</figure>
<figure style={FIGURE}>
<Heading
heading={heading}
courseOverGround={courseOverGround}
headingSetpoint={0.3}
headingSetpointAtZeroDeadband={0.5}
headingSetpointOverride
atHeadingSetpoint={false}
hdgArrowStyle={HdgArrowStyle.beamLine}
cogArrowStyle={CogArrowStyle.velocityVector}
faceDiameter={110}
/>
<figcaption style={CAPTION}>Chart table: beam line, velocity vector, setpoint snapped to north</figcaption>
</figure>
</div>
);
}
Heading is the lighter sibling of the compass. It shows the heading, the course over ground, the heading setpoint and
advice arcs, with no wind, current or rate-of-turn layers. A vessel silhouette or a cluster of center readouts
promotes the face to the double-band ring. It ports obc-heading; the overview lists the other
instruments.
Examples
Heading
Code
import { Heading } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string =
"The heading and course over ground arrows on the double-band card, with the heading setpoint marker.";
export default function HeadingExample(): ReactElement {
return (
<div style={{ width: 200, height: 200 }}>
<Heading heading={47} courseOverGround={52} headingSetpoint={60} />
</div>
);
}
With vessel
Code
import { Heading, VesselImage } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string =
"The vessel silhouette in the center, turning with the heading, and the N, E, S and W letters.";
export default function WithVessel(): ReactElement {
return (
<div style={{ width: 200, height: 200 }}>
<Heading heading={47} courseOverGround={52} vesselImage={VesselImage.genericTop} showLabels />
</div>
);
}
With readouts
Code
import { Heading, CompassReadoutSource, type CompassCenterReadout } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string =
"Heading, course over ground and rate of turn as readouts in the center, on a face pinned to 240 pixels.";
const CENTER_READOUTS: CompassCenterReadout[] = [
{ source: CompassReadoutSource.hdg },
{ source: CompassReadoutSource.cog },
{ source: CompassReadoutSource.rot },
];
export default function WithReadouts(): ReactElement {
return <Heading heading={47} courseOverGround={52} centerReadouts={CENTER_READOUTS} showLabels faceDiameter={240} />;
}
Small face
Code
import { Heading } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string = "A heading card on a face pinned to 180 pixels, with the heading setpoint marker.";
export default function SmallFace(): ReactElement {
return <Heading heading={47} courseOverGround={52} headingSetpoint={60} showLabels faceDiameter={180} />;
}
Measured heading and commanded heading
Name the heading reference and keep course over ground distinct from the bow direction. Supply current heading, accepted setpoint and a draft setpoint as separate values. The focus marker previews an adjustment; it is not confirmation that an autopilot accepted a command.
At-setpoint styling compares supplied values or follows the caller's flag. It does not establish that steering control is engaged. Show control mode and source integrity nearby, and exercise the north crossing so a small angular difference cannot be read as almost a complete turn.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| heading | number | undefined | 0 | The current heading of the vessel in degrees. |
| courseOverGround | number | undefined | 0 | The current course over ground in degrees. |
| headingSetpoint | number | null | undefined | null | The set point for the heading in degrees. |
| newHeadingSetpoint | number | undefined | – | New setpoint being adjusted (renders a second, focus-state marker). |
| atHeadingSetpoint | boolean | undefined | false | Manual at-setpoint flag; used when autoAtHeadingSetpoint is false. |
| headingSetpointAtZeroDeadband | number | undefined | 0.5 | Zero-snap deadband for the setpoint marker, in degrees. |
| headingSetpointOverride | boolean | undefined | false | Derive the setpoint color from priority even when loading/off. |
| autoAtHeadingSetpoint | boolean | undefined | true | Enables automatic at-setpoint calculation. |
| autoAtHeadingSetpointDeadband | number | undefined | 2 | The deadband for auto at-setpoint detection in degrees. |
| animateSetpoint | boolean | undefined | false | Animate setpoint confirm transitions. |
| touching | boolean | undefined | false | Indicates the user is actively adjusting (focus rendering). |
| headingAdvices | AngleAdvice[] | undefined | [] | Caution/alert arcs; triggered state derives from the current heading. |
| vesselImage | VesselImage | undefined | – | The vessel silhouette shown in the center, rotating with heading. When
unset (default) no vessel is shown; hidden while centerReadouts is
non-empty. |
| centerReadouts | CompassCenterReadout[] | undefined | [] | Center readouts replacing the vessel: the first entry renders on top, the
rest side by side below a horizontal divider. Values bind per entry
source (hdg → heading, cog → courseOverGround; rot has no
input on this instrument and renders a dash) and colors follow
priorityElements. While non-empty, the crosshair's center is cut out so
the readouts sit on a clean face. |
| hdgArrowStyle | HdgArrowStyle | undefined | HdgArrowStyle.needle | HDG arrow style: needle (default), arrowHead, vector, or beamLine. |
| cogArrowStyle | CogArrowStyle | undefined | CogArrowStyle.needle | COG arrow style: needle (default), arrowHead, vector, or velocityVector. |
| direction | CompassDirection | undefined | CompassDirection.NorthUp | Orientation mode: north-up (default), heading-up, or course-up. |
| priority | Priority | undefined | Priority.regular | Priority.enhanced uses the blue palette for the elements in priorityElements. |
| priorityElements | HeadingPriorityElement[] | undefined | [HeadingPriorityElement.hdg] | Which elements the enhanced priority applies to (default: hdg). |
| showLabels | boolean | undefined | false | Show compass NSEW labels. The north arrow is always shown, independent of this flag. |
| tickmarksInside | boolean | undefined | false | When true, labels and north arrow are placed inside the outer ring. |
| faceDiameter | number | undefined | – | Outer-ring diameter in CSS pixels. When set, the instrument renders at a fixed intrinsic size derived from the ring, arc shape and label reserve — so instruments sharing the same value have identical ring circumference regardless of label width or arc extent. When unset (default), the instrument fills its container. |
| className | string | undefined | – | Class names added to the instrument's root element. |
Related
- Compass: the same card with the wind, current and rate-of-turn layers.
- Data integrity: the states a value can be in, and what the crew does.