mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Draw flight plan paths in the react UI.
https://github.com/dcs-liberation/dcs_liberation/issues/2039
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import "./App.css";
|
||||
|
||||
import { LiberationMap } from "./map/liberationmap/LiberationMap";
|
||||
import { ControlPoint } from "./game/controlpoint";
|
||||
import { useEffect } from "react";
|
||||
import { useAppDispatch } from "./app/hooks";
|
||||
import { setControlPoints } from "./game/theater/theaterSlice";
|
||||
import { Flight } from "./game/flight";
|
||||
import { LatLng } from "leaflet";
|
||||
import { LiberationMap } from "./map/liberationmap/LiberationMap";
|
||||
import axios from "axios";
|
||||
import { registerFlight } from "./game/ato/atoSlice";
|
||||
import { setControlPoints } from "./game/theater/theaterSlice";
|
||||
import { useAppDispatch } from "./app/hooks";
|
||||
import { useEffect } from "react";
|
||||
|
||||
function App() {
|
||||
const mapCenter: LatLng = new LatLng(25.58, 54.9);
|
||||
@@ -22,6 +24,16 @@ function App() {
|
||||
dispatch(setControlPoints(response.data as ControlPoint[]));
|
||||
}
|
||||
});
|
||||
axios
|
||||
.get("http://[::1]:5000/flights?with_waypoints=true")
|
||||
.catch((error) => console.log(`Error fetching flights: ${error}`))
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
for (const flight of response.data) {
|
||||
dispatch(registerFlight(flight as Flight));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log(`mapCenter=${mapCenter}`);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit";
|
||||
import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit";
|
||||
|
||||
import atoReducer from "../game/ato/atoSlice";
|
||||
import theaterReducer from "../game/theater/theaterSlice";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
atos: atoReducer,
|
||||
theater: theaterReducer,
|
||||
},
|
||||
});
|
||||
|
||||
53
client/src/game/ato/atoSlice.ts
Normal file
53
client/src/game/ato/atoSlice.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
import { Flight } from "../flight";
|
||||
import { RootState } from "../../app/store";
|
||||
|
||||
interface AtoState {
|
||||
blue: { [id: string]: Flight };
|
||||
red: { [id: string]: Flight };
|
||||
}
|
||||
|
||||
const initialState: AtoState = {
|
||||
blue: {},
|
||||
red: {},
|
||||
};
|
||||
|
||||
export const atoSlice = createSlice({
|
||||
name: "ato",
|
||||
initialState,
|
||||
reducers: {
|
||||
clearFlights: (state) => {
|
||||
state.blue = {};
|
||||
state.red = {};
|
||||
},
|
||||
registerFlight: (state, action: PayloadAction<Flight>) => {
|
||||
const flight = action.payload;
|
||||
const ato = flight.blue ? state.blue : state.red;
|
||||
if (flight.id in ato) {
|
||||
console.log(`Overriding flight with ID: ${flight.id}`);
|
||||
}
|
||||
ato[flight.id] = flight;
|
||||
},
|
||||
unregisterFlight: (state, action: PayloadAction<string>) => {
|
||||
const id = action.payload;
|
||||
if (id in state.blue) {
|
||||
delete state.blue[id];
|
||||
} else if (id in state.red) {
|
||||
delete state.red[id];
|
||||
} else {
|
||||
console.log(
|
||||
`Could not delete flight with ID ${id} because no flight with that ` +
|
||||
`ID exists`
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { clearFlights, registerFlight, unregisterFlight } =
|
||||
atoSlice.actions;
|
||||
|
||||
export const selectAtos = (state: RootState) => state.atos;
|
||||
|
||||
export default atoSlice.reducer;
|
||||
10
client/src/game/flight.ts
Normal file
10
client/src/game/flight.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { Waypoint } from "./waypoint";
|
||||
|
||||
export interface Flight {
|
||||
id: string;
|
||||
blue: boolean;
|
||||
position: LatLng;
|
||||
sidc: string;
|
||||
waypoints: Waypoint[] | null;
|
||||
}
|
||||
11
client/src/game/waypoint.ts
Normal file
11
client/src/game/waypoint.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
export interface Waypoint {
|
||||
name: string;
|
||||
position: LatLng;
|
||||
altitude_ft: number;
|
||||
altitude_reference: string;
|
||||
is_movable: boolean;
|
||||
should_mark: boolean;
|
||||
include_in_path: boolean;
|
||||
}
|
||||
33
client/src/map/flightplan/FlightPlan.tsx
Normal file
33
client/src/map/flightplan/FlightPlan.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Flight } from "../../game/flight";
|
||||
import { Polyline } from "react-leaflet";
|
||||
|
||||
const BLUE_PATH = "#0084ff";
|
||||
const RED_PATH = "#c85050";
|
||||
|
||||
interface FlightPlanProps {
|
||||
flight: Flight;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
export function FlightPlanPath(props: FlightPlanProps) {
|
||||
const color = props.flight.blue ? BLUE_PATH : RED_PATH;
|
||||
const waypoints = props.flight.waypoints;
|
||||
if (waypoints == null) {
|
||||
return <></>;
|
||||
}
|
||||
const points = waypoints.map((waypoint) => waypoint.position);
|
||||
return (
|
||||
<Polyline
|
||||
positions={points}
|
||||
pathOptions={{ color: color, interactive: false }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function FlightPlan(props: FlightPlanProps) {
|
||||
return (
|
||||
<>
|
||||
<FlightPlanPath {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
20
client/src/map/flightplanslayer/FlightPlansLayer.tsx
Normal file
20
client/src/map/flightplanslayer/FlightPlansLayer.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { FlightPlan } from "../flightplan/FlightPlan";
|
||||
import { LayerGroup } from "react-leaflet";
|
||||
import { selectAtos } from "../../game/ato/atoSlice";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
|
||||
interface FlightPlansLayerProps {
|
||||
blue: boolean;
|
||||
}
|
||||
|
||||
export function FlightPlansLayer(props: FlightPlansLayerProps) {
|
||||
const atos = useAppSelector(selectAtos);
|
||||
const flights = props.blue ? atos.blue : atos.red;
|
||||
return (
|
||||
<LayerGroup>
|
||||
{Object.values(flights).map((flight) => {
|
||||
return <FlightPlan key={flight.id} flight={flight} selected={false} />;
|
||||
})}
|
||||
</LayerGroup>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { BasemapLayer } from "react-esri-leaflet";
|
||||
import { ControlPointsLayer } from "../controlpointslayer/ControlPointsLayer";
|
||||
import "./LiberationMap.css";
|
||||
import { LatLng } from "leaflet";
|
||||
import { FlightPlansLayer } from "../flightplanslayer/FlightPlansLayer";
|
||||
|
||||
interface GameProps {
|
||||
mapCenter: LatLng;
|
||||
@@ -13,6 +14,8 @@ export function LiberationMap(props: GameProps) {
|
||||
<MapContainer zoom={8} center={props.mapCenter}>
|
||||
<BasemapLayer name="ImageryClarity" />
|
||||
<ControlPointsLayer />
|
||||
<FlightPlansLayer blue={true} />
|
||||
<FlightPlansLayer blue={false} />
|
||||
</MapContainer>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user