Skip to content
design
Instruments

Depth actual

The water under the vessel, with a seabed that rises as the depth shrinks and a gauge that marks it.

On this page
1234567891011121314
14.0
Depth
m
Echosounder
11.8
Under keel
m
Draft 2.2 m

Plenty of water: keep the approach speed under 3 kn.

1234567891011121314
‒
Depth
m
Forward sonar

Forward sonar lost the bottom in the weed: steer by the echosounder until it reads again.

A sailing yacht's draft band, shallow-water caution and advice ranges, custom range and tickmarks and the enhanced palette on a live approach to an anchorage, beside a sounder that has lost the bottom.
Code
import { AdviceType, DepthActual, Priority, Readout, ReadoutDataQuality, VesselImage } from "@bosunkit/instruments";
import type { LinearAdvice } from "@bosunkit/instruments";
import { useEffect, useState } from "react";
import type { CSSProperties, Dispatch, ReactElement, SetStateAction } from "react";

export const description: string =
  "Althea feeling her way into the anchorage at Cala Galdana on a one-second sounding feed, with a shallow-water caution range that triggers under 3.5 m and an advice range above it, beside a second sounder that has lost its bottom.";

const DRAFT_METERS: number = 2.2;
/** Feed steps for one approach and back out. */
const CYCLE: number = 40;

const SHALLOW_ADVICE: LinearAdvice[] = [
  { min: 0, max: 3.5, type: AdviceType.caution, hinted: false },
  { min: 3.5, max: 5, type: AdviceType.advice, hinted: true },
];

const CAPTION_STYLE: CSSProperties = { margin: 0, fontSize: 12, color: "var(--element-neutral-color)" };

/** The sounding over the approach: 14 m at the entrance, shoaling to 3.1 m off the beach, then back out. */
function depthAt(step: number): number {
  const progress: number = step < CYCLE / 2 ? step / (CYCLE / 2) : (CYCLE - step) / (CYCLE / 2);
  return 14 - 10.9 * progress + 0.15 * Math.sin(step * 1.7);
}

export default function Showcase(): ReactElement {
  const [step, setStep]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0);

  useEffect((): (() => void) => {
    const timer: number = window.setInterval((): void => {
      setStep((previous: number): number => (previous + 1) % CYCLE);
    }, 1000);
    return (): void => {
      window.clearInterval(timer);
    };
  }, []);

  const depth: number = depthAt(step);
  const underKeel: number = depth - DRAFT_METERS;
  const note: string =
    depth <= 3.5
      ? `Under keel ${underKeel.toFixed(1)} m: stop here and anchor, or turn back to deeper water.`
      : depth <= 5
        ? "Approaching the 3.5 m limit: slow down and prepare the anchor."
        : "Plenty of water: keep the approach speed under 3 kn.";

  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 24, justifyContent: "center", alignItems: "flex-start" }}>
      <div style={{ display: "grid", gap: 8, justifyItems: "center", width: 300 }}>
        <div style={{ width: 280, height: 280 }}>
          <DepthActual
            depth={depth}
            draft={DRAFT_METERS}
            advice={SHALLOW_ADVICE}
            maxDepth={15}
            primaryTickmarkInterval={5}
            secondaryTickmarkInterval={1}
            vesselImage={VesselImage.sailingYachtFore}
            priority={Priority.enhanced}
          />
        </div>
        <div style={{ display: "flex", gap: 24 }}>
          <Readout label="Depth" unit="m" src="Echosounder" value={depth} fractionDigits={1} />
          <Readout label="Under keel" unit="m" src="Draft 2.2 m" value={underKeel} fractionDigits={1} />
        </div>
        <p style={{ ...CAPTION_STYLE, textAlign: "center" }}>{note}</p>
      </div>
      <div style={{ display: "grid", gap: 8, justifyItems: "center", width: 200 }}>
        <div style={{ width: 180, height: 180 }}>
          <DepthActual
            depth={Number.NaN}
            draft={DRAFT_METERS}
            advice={SHALLOW_ADVICE}
            maxDepth={15}
            primaryTickmarkInterval={5}
            secondaryTickmarkInterval={1}
            vesselImage={VesselImage.sailingYachtFore}
          />
        </div>
        <Readout label="Depth" unit="m" src="Forward sonar" value={null} dataQuality={ReadoutDataQuality.invalid} />
        <p style={{ ...CAPTION_STYLE, textAlign: "center" }}>
          Forward sonar lost the bottom in the weed: steer by the echosounder until it reads again.
        </p>
      </div>
    </div>
  );
}

Depth actual draws the water column under the vessel. The seabed rises as the depth shrinks, the vessel floats at its draft, and a linear gauge column maps the depth on a scale from the surface to maxDepth, or with none set to the first rung of the range ladder, 25 meters. A depth beyond the range is drawn at the end of the scale. The draft is the dark band at the top of the gauge. advice marks shallow-water ranges and turns one to triggered when the depth is inside it.

The instrument fills the box it is given, so the example sets one.

Examples

Depth

5101520253035404550556065707580859095
A depth of 12.4 meters and a draft of 2.2 meters on the default 10 meter scale.
Code
import { DepthActual } from "@bosunkit/instruments";
import type { ReactElement } from "react";

export const description: string = "A depth of 12.4 meters and a draft of 2.2 meters on the default 10 meter scale.";

export default function Depth(): ReactElement {
  return (
    <div style={{ width: 200, height: 200 }}>
      <DepthActual depth={12.4} draft={2.2} />
    </div>
  );
}

Surface depth and under-keel clearance

depth is depth below the surface in meters, and draft is the vessel's draft in meters. Normalize a transducer reading to that reference before passing it in; do not feed under-keel clearance into the surface-depth prop. If a separate readout shows clearance, label it explicitly and use the same vessel offsets and source data.

A value beyond maxDepth reaches the end of the drawing, so retain the numeric reading when exact depth matters. Choose advice ranges from the vessel's operational limits, not from a convenient visual scale. Treat a lost bottom or invalid sample as unavailable; omitting the prop selects its default value and does not declare missing telemetry. Follow the no-reading example and expose source quality in the containing panel.

Props

PropTypeDefaultDescription
depthnumber | undefined0Depth below the surface in meters.
draftnumber | undefined0Vessel draft in meters — drawn as the dark band at the top of the gauge.
adviceLinearAdvice[] | undefined[]Shallow-water advice zones; triggered when the depth is inside the range.
vesselScalenumber | undefined1Factor on the range's vessel size.
vesselImageVesselImage | undefinedVesselImage.psvForeFore-view vessel silhouette floating at the waterline.
priorityPriority | undefinedPriority.regularRegular or enhanced palette of the depth fill and the draft band.
showLabelsboolean | undefinedtrueLabel the scale ends and the primary ladder beside the frame. Default true.
rangesreadonly DepthRange[] | undefinedDEPTH_RANGESRange ladder shared with the other depth instruments. Defaults to the design's three rungs.
maxDepthnumber | undefined–Explicit scale maximum; wins over autoRange.
autoRangeboolean | undefinedfalseSteps the ladder with depth.
primaryTickmarkIntervalnumber | undefined–Distance between the labeled tickmarks on the depth scale, in meters; overrides the range's.
secondaryTickmarkIntervalnumber | undefined–Distance between the secondary tickmarks on the depth scale, in meters; overrides the range's.
classNamestring | undefined–Class name added to the root element.