diff --git a/client/src/components/flightplan/FlightPlan.tsx b/client/src/components/flightplan/FlightPlan.tsx
index 7ef7375c..6fce8100 100644
--- a/client/src/components/flightplan/FlightPlan.tsx
+++ b/client/src/components/flightplan/FlightPlan.tsx
@@ -1,5 +1,6 @@
import { Flight } from "../../api/flight";
import { Polyline } from "react-leaflet";
+import WaypointMarker from "../waypointmarker";
const BLUE_PATH = "#0084ff";
const RED_PATH = "#c85050";
@@ -35,10 +36,33 @@ function FlightPlanPath(props: FlightPlanProps) {
);
}
+const WaypointMarkers = (props: FlightPlanProps) => {
+ if (!props.selected || props.flight.waypoints == null) {
+ return <>>;
+ }
+
+ return (
+ <>
+ {props.flight.waypoints
+ .filter((p) => p.should_mark)
+ .map((p, idx) => {
+ return (
+
+ );
+ })}
+ >
+ );
+};
+
export default function FlightPlan(props: FlightPlanProps) {
return (
<>
+
>
);
}
diff --git a/client/src/components/waypointmarker/WaypointMarker.tsx b/client/src/components/waypointmarker/WaypointMarker.tsx
new file mode 100644
index 00000000..c85f5eac
--- /dev/null
+++ b/client/src/components/waypointmarker/WaypointMarker.tsx
@@ -0,0 +1,76 @@
+import { Marker, Tooltip, useMap, useMapEvent } from "react-leaflet";
+import { MutableRefObject, useCallback, useEffect, useRef } from "react";
+
+import { Icon } from "leaflet";
+import { Marker as LMarker } from "leaflet";
+import { Waypoint } from "../../api/waypoint";
+import icon from "leaflet/dist/images/marker-icon.png";
+import iconShadow from "leaflet/dist/images/marker-shadow.png";
+
+const WAYPOINT_ICON = new Icon({
+ iconUrl: icon,
+ shadowUrl: iconShadow,
+ iconAnchor: [12, 41],
+});
+
+interface WaypointMarkerProps {
+ number: number;
+ waypoint: Waypoint;
+}
+
+const WaypointMarker = (props: WaypointMarkerProps) => {
+ // Most props of react-leaflet types are immutable and components will not
+ // update to account for changes, so we can't simply use the `permanent`
+ // property of the tooltip to control tooltip visibility based on the zoom
+ // level.
+ //
+ // On top of that, listening for zoom changes and opening/closing is not
+ // sufficient because clicking anywhere will close any opened tooltips (even
+ // if they are permanent; once openTooltip has been called that seems to no
+ // longer have any effect).
+ //
+ // Instead, listen for zoom changes and rebind the tooltip when the zoom level
+ // changes.
+ const map = useMap();
+ const marker: MutableRefObject = useRef();
+
+ const rebindTooltip = useCallback(() => {
+ if (marker.current === undefined) {
+ return;
+ }
+
+ const tooltip = marker.current.getTooltip();
+ if (tooltip === undefined) {
+ return;
+ }
+
+ const permanent = map.getZoom() >= 9;
+ marker.current
+ .unbindTooltip()
+ .bindTooltip(tooltip, { permanent: permanent });
+ }, [map]);
+ useMapEvent("zoomend", rebindTooltip);
+
+ const waypoint = props.waypoint;
+ return (
+ {
+ if (ref != null) {
+ marker.current = ref;
+ }
+ }}
+ >
+
+ {`${props.number} ${waypoint.name}`}
+
+ {`${waypoint.altitude_ft} ft ${waypoint.altitude_reference}`}
+
+ TODO: Timing info
+
+
+ );
+};
+
+export default WaypointMarker;
diff --git a/client/src/components/waypointmarker/index.ts b/client/src/components/waypointmarker/index.ts
new file mode 100644
index 00000000..e0797a71
--- /dev/null
+++ b/client/src/components/waypointmarker/index.ts
@@ -0,0 +1,3 @@
+import WaypointMarker from "./WaypointMarker";
+
+export default WaypointMarker;