From 8e20499b921df18e92c96523386f959115b8b9ac Mon Sep 17 00:00:00 2001 From: MarcoJayUsai Date: Fri, 7 Mar 2025 12:00:43 +0100 Subject: [PATCH 1/2] feat(drawings): added navpoints --- .../react/src/map/drawings/drawingsmanager.ts | 69 +++++++++++++++++++ frontend/react/src/map/map.ts | 1 + .../react/src/map/markers/navpointmarker.ts | 48 +++++++++++++ .../src/map/markers/stylesheets/navpoint.css | 27 ++++++++ scripts/lua/backend/OlympusCommand.lua | 24 +++++++ 5 files changed, 169 insertions(+) create mode 100644 frontend/react/src/map/markers/navpointmarker.ts create mode 100644 frontend/react/src/map/markers/stylesheets/navpoint.css diff --git a/frontend/react/src/map/drawings/drawingsmanager.ts b/frontend/react/src/map/drawings/drawingsmanager.ts index 6d193d40..9dc21c32 100644 --- a/frontend/react/src/map/drawings/drawingsmanager.ts +++ b/frontend/react/src/map/drawings/drawingsmanager.ts @@ -3,6 +3,7 @@ import { getApp } from "../../olympusapp"; import { DrawingsInitEvent, DrawingsUpdatedEvent, MapOptionsChangedEvent, SessionDataLoadedEvent } from "../../events"; import { MapOptions } from "../../types/types"; import { Circle, DivIcon, Layer, LayerGroup, layerGroup, Marker, Polygon, Polyline } from "leaflet"; +import { NavpointMarker } from "../markers/navpointmarker"; export abstract class DCSDrawing { #name: string; @@ -452,6 +453,57 @@ export class DCSTextBox extends DCSDrawing { } } +export class DCSNavpoint extends DCSDrawing { + #point: NavpointMarker; + + constructor(drawingData, parent) { + super(drawingData, parent); + + this.#point = new NavpointMarker([drawingData.lat, drawingData.lng], drawingData.callsignStr, drawingData.comment); + + this.setVisibility(true); + } + + getLayer() { + return this.#point; + } + + getLabelLayer() { + return this.#point; + } + + setOpacity(opacity: number): void { + if (opacity === this.#point.options.opacity) return; + + this.#point.options.opacity = opacity; + + /* Hack to force marker redraw */ + const originalVisibility = this.getVisibility(); + this.setVisibility(false); + this.setVisibility(originalVisibility); + + getApp().getDrawingsManager().requestUpdateEventDispatch(); + } + + setVisibility(visibility: boolean): void { + if (visibility && !this.getParent().getLayerGroup().hasLayer(this.#point)) this.#point.addTo(this.getParent().getLayerGroup()); + //@ts-ignore Leaflet typings are wrong + if (!visibility && this.getParent().getLayerGroup().hasLayer(this.#point)) this.#point.removeFrom(this.getParent().getLayerGroup()); + + if (visibility && !this.getParent().getVisibility()) this.getParent().setVisibility(true); + + getApp().getDrawingsManager().requestUpdateEventDispatch(); + } + + getOpacity(): number { + return this.#point.options.opacity ?? 1; + } + + getVisibility(): boolean { + return this.getParent().getLayerGroup().hasLayer(this.#point); + } +} + export class DCSDrawingsContainer { #drawings: DCSDrawing[] = []; #subContainers: DCSDrawingsContainer[] = []; @@ -475,6 +527,9 @@ export class DCSDrawingsContainer { initFromData(drawingsData) { let hasContainers = false; Object.keys(drawingsData).forEach((layerName: string) => { + if (layerName === 'navpoints') { + return; + } if (drawingsData[layerName]["name"] === undefined) { const newContainer = new DCSDrawingsContainer(layerName, this); this.addSubContainer(newContainer); @@ -487,6 +542,7 @@ export class DCSDrawingsContainer { Object.keys(drawingsData).forEach((layerName: string) => { const primitiveType = drawingsData[layerName]["primitiveType"]; + const isANavpoint = !!drawingsData[layerName]['callsignStr']; // Possible primitives: // "Line","TextBox","Polygon","Icon" @@ -499,6 +555,12 @@ export class DCSDrawingsContainer { let newDrawing = new DCSEmptyLayer(drawingsData[layerName], othersContainer) as DCSDrawing; + if (isANavpoint) { + newDrawing = new DCSNavpoint(drawingsData[layerName], othersContainer); + this.addDrawing(newDrawing); + return; + } + switch (primitiveType) { case "Polygon": newDrawing = new DCSPolygon(drawingsData[layerName], othersContainer); @@ -521,6 +583,12 @@ export class DCSDrawingsContainer { if (othersContainer.getDrawings().length === 0) this.removeSubContainer(othersContainer); // Remove empty container } + initNavpoints(drawingsData) { + const newContainer = new DCSDrawingsContainer('Navpoints', this); + this.addSubContainer(newContainer); + newContainer.initFromData(drawingsData); + } + getLayerGroup() { return this.#layerGroup; } @@ -655,6 +723,7 @@ export class DrawingsManager { initDrawings(data: { drawings: Record> }): boolean { if (data && data.drawings) { this.#drawingsContainer.initFromData(data.drawings); + if (data.drawings.navpoints) this.#drawingsContainer.initNavpoints(data.drawings.navpoints); if (this.#sessionDataDrawings["Mission drawings"]) this.#drawingsContainer.fromJSON(this.#sessionDataDrawings["Mission drawings"]); DrawingsInitEvent.dispatch(this.#drawingsContainer); this.#initialized = true; diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts index 1eb296da..bad1cb36 100644 --- a/frontend/react/src/map/map.ts +++ b/frontend/react/src/map/map.ts @@ -37,6 +37,7 @@ import "./markers/stylesheets/bullseye.css"; import "./markers/stylesheets/units.css"; import "./markers/stylesheets/spot.css"; import "./markers/stylesheets/measure.css"; +import "./markers/stylesheets/navpoint.css"; import "./stylesheets/map.css"; import { initDraggablePath } from "./coalitionarea/draggablepath"; diff --git a/frontend/react/src/map/markers/navpointmarker.ts b/frontend/react/src/map/markers/navpointmarker.ts new file mode 100644 index 00000000..f7092794 --- /dev/null +++ b/frontend/react/src/map/markers/navpointmarker.ts @@ -0,0 +1,48 @@ +import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet"; +import { CustomMarker } from "./custommarker"; + +export class NavpointMarker extends CustomMarker { + #callsignStr: string; + #comment: string; + + constructor(latlng: LatLngExpression, callsignStr: string, comment?: string) { + super(latlng, { interactive: false, draggable: false }); + this.#callsignStr = callsignStr; + comment ? this.#comment = comment : null; + } + + createIcon() { + /* Set the icon */ + let icon = new DivIcon({ + className: "leaflet-navpoint-icon", + iconAnchor: [0, 0], + iconSize: [50, 50], + }); + this.setIcon(icon); + + let el = document.createElement("div"); + el.classList.add("navpoint"); + + // Main icon + let pointIcon = document.createElement("div"); + pointIcon.classList.add("navpoint-icon"); + el.append(pointIcon); + + // Label + let mainLabel: HTMLDivElement = document.createElement("div");; + mainLabel.classList.add("navpoint-main-label"); + mainLabel.innerText = this.#callsignStr; + el.append(mainLabel); + + // Further description + if (this.#comment) { + let commentBox: HTMLDivElement = document.createElement("div");; + commentBox.classList.add("navpoint-comment-box"); + commentBox.innerText = this.#comment; + mainLabel.append(commentBox); + } + + this.getElement()?.appendChild(el); + this.getElement()?.classList.add("ol-navpoint-marker"); + } +} diff --git a/frontend/react/src/map/markers/stylesheets/navpoint.css b/frontend/react/src/map/markers/stylesheets/navpoint.css new file mode 100644 index 00000000..a3e30eb9 --- /dev/null +++ b/frontend/react/src/map/markers/stylesheets/navpoint.css @@ -0,0 +1,27 @@ +.ol-navpoint-marker>.navpoint { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; +} +.ol-navpoint-marker>.navpoint>.navpoint-icon { + height: 8px; + width: 8px; + background: white; + flex: none; + transform: rotate3d(0, 0, 1, 45deg); +} + +.ol-navpoint-marker>.navpoint>.navpoint-main-label { + display: flex; + flex-direction: column; + font-size: 10px; + color: white; +} + +.ol-navpoint-marker .navpoint-comment-box { + font-size: 8px; + font-style: italic; + color: white; + max-width: 50px; +} \ No newline at end of file diff --git a/scripts/lua/backend/OlympusCommand.lua b/scripts/lua/backend/OlympusCommand.lua index c3d68cef..12377fad 100644 --- a/scripts/lua/backend/OlympusCommand.lua +++ b/scripts/lua/backend/OlympusCommand.lua @@ -1076,6 +1076,26 @@ function getUnitDescription(unit) return unit:getDescr() end +-- This function gets the navpoints from the DCS mission +function Olympus.getNavPoints() + local navpoints = {} + if mist.DBs.navPoints ~= nil then + for coalitionName, coalitionNavpoints in pairs(mist.DBs.navPoints) do + for index, navpointDrawingData in pairs(coalitionNavpoints) do + -- Let's convert DCS coords to lat lon + local vec3 = { x = navpointDrawingData['x'], y = 0, z = navpointDrawingData['y'] } + local lat, lng = coord.LOtoLL(vec3) + navpointDrawingData['lat'] = lat + navpointDrawingData['lng'] = lng + navpointDrawingData['coalition'] = coalitionName + end + navpoints[coalitionName] = coalitionNavpoints + end + end + + return navpoints +end + -- This function is periodically called to collect the data of all the existing drawings in the mission to be transmitted to the olympus.dll function Olympus.initializeDrawings() local drawings = {} @@ -1129,6 +1149,10 @@ function Olympus.initializeDrawings() end end + local navpoints = Olympus.getNavPoints() + + drawings['navpoints'] = navpoints + Olympus.drawingsByLayer["drawings"] = drawings -- Send the drawings to the DLL From 6e7b5b1cc3c6bcde4027f353d7d5e02a47a3fc55 Mon Sep 17 00:00:00 2001 From: MarcoJayUsai Date: Fri, 7 Mar 2025 12:36:10 +0100 Subject: [PATCH 2/2] feat(navpoints): added navpoint sublayers --- .../react/src/map/drawings/drawingsmanager.ts | 6 +++-- scripts/lua/backend/OlympusCommand.lua | 22 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/frontend/react/src/map/drawings/drawingsmanager.ts b/frontend/react/src/map/drawings/drawingsmanager.ts index 9dc21c32..af74ffce 100644 --- a/frontend/react/src/map/drawings/drawingsmanager.ts +++ b/frontend/react/src/map/drawings/drawingsmanager.ts @@ -530,7 +530,7 @@ export class DCSDrawingsContainer { if (layerName === 'navpoints') { return; } - if (drawingsData[layerName]["name"] === undefined) { + if (drawingsData[layerName]["name"] === undefined && drawingsData[layerName]["callsignStr"] === undefined) { const newContainer = new DCSDrawingsContainer(layerName, this); this.addSubContainer(newContainer); newContainer.initFromData(drawingsData[layerName]); @@ -557,7 +557,9 @@ export class DCSDrawingsContainer { if (isANavpoint) { newDrawing = new DCSNavpoint(drawingsData[layerName], othersContainer); - this.addDrawing(newDrawing); + if (hasContainers) othersContainer.addDrawing(newDrawing); + else this.addDrawing(newDrawing); + if (othersContainer.getDrawings().length === 0) this.removeSubContainer(othersContainer); // Remove empty container return; } diff --git a/scripts/lua/backend/OlympusCommand.lua b/scripts/lua/backend/OlympusCommand.lua index 12377fad..af9f1ce9 100644 --- a/scripts/lua/backend/OlympusCommand.lua +++ b/scripts/lua/backend/OlympusCommand.lua @@ -1077,19 +1077,37 @@ function getUnitDescription(unit) end -- This function gets the navpoints from the DCS mission -function Olympus.getNavPoints() +function Olympus.getNavPoints() + local function extract_tag(str) + return str:match("^%[(.-)%]") + end + local navpoints = {} if mist.DBs.navPoints ~= nil then for coalitionName, coalitionNavpoints in pairs(mist.DBs.navPoints) do + if navpoints[coalitionName] == nil then + navpoints[coalitionName] = {} + end + for index, navpointDrawingData in pairs(coalitionNavpoints) do + local navpointCustomLayer = extract_tag(navpointDrawingData['callsignStr']); + -- Let's convert DCS coords to lat lon local vec3 = { x = navpointDrawingData['x'], y = 0, z = navpointDrawingData['y'] } local lat, lng = coord.LOtoLL(vec3) navpointDrawingData['lat'] = lat navpointDrawingData['lng'] = lng navpointDrawingData['coalition'] = coalitionName + + if navpointCustomLayer ~= nil then + if navpoints[coalitionName][navpointCustomLayer] == nil then + navpoints[coalitionName][navpointCustomLayer] = {} + end + navpoints[coalitionName][navpointCustomLayer][navpointDrawingData['callsignStr']] = navpointDrawingData + else + navpoints[coalitionName][navpointDrawingData['callsignStr']] = navpointDrawingData + end end - navpoints[coalitionName] = coalitionNavpoints end end