15 Commits

Author SHA1 Message Date
Pax1601
182fe294de Merge pull request #995 from Pax1601/features/v1/unit-coordinates-copy
feat(map): unit coordinates are now copied entirely. Added info poup.
2024-12-17 15:18:34 +01:00
MarcoJayUsai
95db9b5d38 feat(map): unit coordinates are now copied entirely. Added info poup. 2024-12-13 15:15:23 +01:00
Pax1601
a6e28e9064 Merge pull request #948 from Pax1601/bugfix/v1/wrong-unit-on-reconnection
Bugfix/v1/wrong unit on reconnection
2024-11-25 10:17:14 +01:00
Pax1601
116d4fa894 Merge pull request #949 from Pax1601/feature/v1/selected-unit-coords
Feature/v1/selected unit coords
2024-11-23 17:05:29 +01:00
MarcoJayUsai
8b5df691ec fix(selected unit coords): failsafe in case of absence of the navigator element 2024-11-22 15:14:57 +01:00
MarcoJayUsai
f00fce7fe7 fix(selected unit coords): added minutes indication in latlngdec coordinates format
In the coordinates panel, a lat lng with decimal minutes would appear without the ending " ' " character.
2024-11-22 15:10:44 +01:00
MarcoJayUsai
6bb150a41c feat(map): added selected air units FL in unit coordinates panel; added new coordinates system 2024-11-20 12:50:56 +01:00
MarcoJayUsai
2cadebcabd feat(map): click selected unit coordinates panel will now change location system 2024-11-20 11:43:10 +01:00
MarcoJayUsai
b61289d996 fix(mouseinfopanel): switched to right mouse button to copy coordinates, added tip 2024-11-20 11:27:40 +01:00
MarcoJayUsai
89051c3e85 refactor(unit): moved data update code from initialize to update; made initialize and update final 2024-11-19 13:58:16 +01:00
MarcoJayUsai
064c24e023 fix(map): reconnecting players will not be displayed with wrong name or wrong coalition 2024-11-18 21:56:01 +01:00
MarcoJayUsai
e273203629 feat(map): selected unit coordinates panel moved to top; coords are now copy-able 2024-11-16 18:33:59 +01:00
Pax1601
17b10bebd5 Merge pull request #943 from Pax1601/feature/human-original-callsign
feat(map): added an option to show human controlled unit original callsign
2024-11-13 09:33:51 +01:00
MarcoJayUsai
5e5ee30b8f feat(mouse info panel): added selected unit coordinates displaying
The coordinates are only shown if one unit has been selected.
2024-11-11 18:56:29 +01:00
MarcoJayUsai
5cd566c7ca feat(map): added an option to show human controlled unit original callsign 2024-11-11 12:19:05 +01:00
13 changed files with 350 additions and 52 deletions

View File

@@ -13,6 +13,7 @@ namespace DataIndex {
country, country,
name, name,
unitName, unitName,
callsign,
groupName, groupName,
state, state,
task, task,

View File

@@ -19,12 +19,12 @@ public:
~Unit(); ~Unit();
/********** Methods **********/ /********** Methods **********/
void initialize(json::value json); virtual void initialize(json::value json) final;
virtual void setDefaults(bool force = false); virtual void setDefaults(bool force = false);
void runAILoop(); void runAILoop();
void update(json::value json, double dt); virtual void update(json::value json, double dt) final;
void refreshLeaderData(unsigned long long time); void refreshLeaderData(unsigned long long time);
unsigned int getID() { return ID; } unsigned int getID() { return ID; }
@@ -71,6 +71,7 @@ public:
virtual void setCountry(unsigned char newValue) { updateValue(country, newValue, DataIndex::country); } virtual void setCountry(unsigned char newValue) { updateValue(country, newValue, DataIndex::country); }
virtual void setName(string newValue) { updateValue(name, newValue, DataIndex::name); } virtual void setName(string newValue) { updateValue(name, newValue, DataIndex::name); }
virtual void setUnitName(string newValue) { updateValue(unitName, newValue, DataIndex::unitName); } virtual void setUnitName(string newValue) { updateValue(unitName, newValue, DataIndex::unitName); }
virtual void setCallsign(string newValue) { updateValue(callsign, newValue, DataIndex::callsign); }
virtual void setGroupName(string newValue) { updateValue(groupName, newValue, DataIndex::groupName); } virtual void setGroupName(string newValue) { updateValue(groupName, newValue, DataIndex::groupName); }
virtual void setState(unsigned char newValue) { updateValue(state, newValue, DataIndex::state); }; virtual void setState(unsigned char newValue) { updateValue(state, newValue, DataIndex::state); };
virtual void setTask(string newValue) { updateValue(task, newValue, DataIndex::task); } virtual void setTask(string newValue) { updateValue(task, newValue, DataIndex::task); }
@@ -117,6 +118,7 @@ public:
virtual unsigned char getCoalition() { return coalition; } virtual unsigned char getCoalition() { return coalition; }
virtual unsigned char getCountry() { return country; } virtual unsigned char getCountry() { return country; }
virtual string getName() { return name; } virtual string getName() { return name; }
virtual string getCallsign() { return callsign; }
virtual string getUnitName() { return unitName; } virtual string getUnitName() { return unitName; }
virtual string getGroupName() { return groupName; } virtual string getGroupName() { return groupName; }
virtual unsigned char getState() { return state; } virtual unsigned char getState() { return state; }
@@ -167,6 +169,7 @@ protected:
unsigned char country = NULL; unsigned char country = NULL;
string name = ""; string name = "";
string unitName = ""; string unitName = "";
string callsign = "";
string groupName = ""; string groupName = "";
unsigned char state = State::NONE; unsigned char state = State::NONE;
string task = ""; string task = "";

View File

@@ -27,6 +27,13 @@ Unit::~Unit()
} }
void Unit::initialize(json::value json) void Unit::initialize(json::value json)
{
update(json, 0);
setDefaults();
}
void Unit::update(json::value json, double dt)
{ {
if (json.has_string_field(L"name")) if (json.has_string_field(L"name"))
setName(to_string(json[L"name"])); setName(to_string(json[L"name"]));
@@ -37,23 +44,18 @@ void Unit::initialize(json::value json)
if (json.has_string_field(L"groupName")) if (json.has_string_field(L"groupName"))
setGroupName(to_string(json[L"groupName"])); setGroupName(to_string(json[L"groupName"]));
if (json.has_string_field(L"callsign"))
setCallsign(to_string(json[L"callsign"]));
if (json.has_number_field(L"coalitionID")) if (json.has_number_field(L"coalitionID"))
setCoalition(json[L"coalitionID"].as_number().to_int32()); setCoalition(json[L"coalitionID"].as_number().to_int32());
//if (json.has_number_field(L"Country")) //if (json.has_number_field(L"Country"))
// setCountry(json[L"Country"].as_number().to_int32()); // setCountry(json[L"Country"].as_number().to_int32());
/* All units which contain the name "Olympus" are automatically under AI control */ /* All units which contain the name "Olympus" are automatically under AI control */
if (getUnitName().find("Olympus") != string::npos) if (getUnitName().find("Olympus") != string::npos)
setControlled(true); setControlled(true);
update(json, 0);
setDefaults();
}
void Unit::update(json::value json, double dt)
{
if (json.has_object_field(L"position")) if (json.has_object_field(L"position"))
{ {
setPosition({ setPosition({
@@ -255,6 +257,7 @@ void Unit::getData(stringstream& ss, unsigned long long time)
case DataIndex::country: appendNumeric(ss, datumIndex, country); break; case DataIndex::country: appendNumeric(ss, datumIndex, country); break;
case DataIndex::name: appendString(ss, datumIndex, name); break; case DataIndex::name: appendString(ss, datumIndex, name); break;
case DataIndex::unitName: appendString(ss, datumIndex, unitName); break; case DataIndex::unitName: appendString(ss, datumIndex, unitName); break;
case DataIndex::callsign: appendString(ss, datumIndex, callsign); break;
case DataIndex::groupName: appendString(ss, datumIndex, groupName); break; case DataIndex::groupName: appendString(ss, datumIndex, groupName); break;
case DataIndex::state: appendNumeric(ss, datumIndex, state); break; case DataIndex::state: appendNumeric(ss, datumIndex, state); break;
case DataIndex::task: appendString(ss, datumIndex, task); break; case DataIndex::task: appendString(ss, datumIndex, task); break;

View File

@@ -160,6 +160,8 @@
column-gap: 6px; column-gap: 6px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: column;
align-items: flex-end;
font-size: 11px; font-size: 11px;
font-weight: bold; font-weight: bold;
justify-content: right; justify-content: right;
@@ -173,18 +175,22 @@
-1px 1px 0 #000, -1px 1px 0 #000,
1px 1px 0 #000; 1px 1px 0 #000;
right: 100%; right: 100%;
width: fit-content; width: max-content;
} }
[data-hide-labels] [data-object|="unit"] .unit-summary { [data-hide-labels] [data-object|="unit"] .unit-summary {
display: none; display: none;
} }
[data-hide-original-callsign] [data-object|="unit"] .unit-original-callsign {
display: none;
}
[data-object|="unit"] .unit-summary>* { [data-object|="unit"] .unit-summary>* {
padding: 1px; padding: 1px;
} }
[data-object|="unit"] .unit-summary .unit-callsign { [data-object|="unit"] .unit-summary .unit-callsign .unit-group {
color: white; color: white;
overflow: hidden; overflow: hidden;
text-align: right; text-align: right;

View File

@@ -104,7 +104,76 @@
} }
#coordinates-tool[data-location-system="LatLng"] [data-location-system="LatLng"], #coordinates-tool[data-location-system="LatLng"] [data-location-system="LatLng"],
#coordinates-tool[data-location-system="LatLngDec"] [data-location-system="LatLngDec"],
#coordinates-tool[data-location-system="MGRS"] [data-location-system="MGRS"], #coordinates-tool[data-location-system="MGRS"] [data-location-system="MGRS"],
#coordinates-tool[data-location-system="UTM"] [data-location-system="UTM"] { #coordinates-tool[data-location-system="UTM"] [data-location-system="UTM"] {
display:flex; display:flex;
}
/* Unit Coordinates */
#unit-coordinates .elevation::after {
content: attr(data-value)
}
#unit-coordinates[data-location-system] [data-location-system] {
cursor:pointer;
display:none;
}
#unit-coordinates[data-location-system="LatLng"] [data-location-system="LatLng"],
#unit-coordinates[data-location-system="LatLngDec"] [data-location-system="LatLngDec"],
#unit-coordinates[data-location-system="MGRS"] [data-location-system="MGRS"],
#unit-coordinates[data-location-system="UTM"] [data-location-system="UTM"] {
display:flex;
}
#unit-coordinates-container {
box-sizing: border-box;
background-color: var(--background-steel);
border-radius: var(--border-radius-sm);
box-shadow: 0px 2px 5px #000A;
padding: 10px;
position: absolute;
top: -48px;
right: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 0px;
transition: all 200ms ease-in-out;
}
#unit-coordinates-container[data-open="true"] {
gap: 4px;
top: -140px;
}
#unit-coordinates-container > #unit-coordinates {
width: 170px;
height: 0;
padding: 0 6px;
overflow: hidden;
flex-grow: 1;
transition: all 200ms ease-in-out;
}
#unit-coordinates-container[data-open="true"] > #unit-coordinates {
height: 90px;
padding: 6px;
}
#unit-coordinates-container > #unit-coordinates-toggle {
font-size: 10px;
flex-shrink: 1;
cursor: pointer;
}
#unit-coordinates-container > #unit-coordinates-toggle> #unit-coordinates-toggle-icon::after {
margin-top: 8px;
font-size: 12px;
content: "↑";
}
#unit-coordinates-container[data-open="true"] > #unit-coordinates-toggle > #unit-coordinates-toggle-icon::after {
content: "↓";
} }

View File

@@ -42,6 +42,16 @@
<div id="mouse-position-longitude" class="coordinates" data-location-system="LatLng"></div> <div id="mouse-position-longitude" class="coordinates" data-location-system="LatLng"></div>
</div> </div>
<div class="mouse-tool-item" data-location-system="LatLngDec">
<div id="ref-mouse-position-latitude-dec" data-location-system="LatLngDec"></div>
<div id="mouse-position-latitude-dec" class="coordinates" data-location-system="LatLngDec"></div>
</div>
<div class="mouse-tool-item" data-location-system="LatLngDec">
<div id="ref-mouse-position-longitude-dec" data-location-system="LatLngDec"></div>
<div id="mouse-position-longitude-dec" class="coordinates" data-location-system="LatLngDec"></div>
</div>
<div class="mouse-tool-item" data-location-system="UTM"> <div class="mouse-tool-item" data-location-system="UTM">
<div id="ref-mouse-position-utm-northing" data-location-system="UTM"></div> <div id="ref-mouse-position-utm-northing" data-location-system="UTM"></div>
<div id="mouse-position-utm-northing" class="coordinates" data-location-system="UTM"></div> <div id="mouse-position-utm-northing" class="coordinates" data-location-system="UTM"></div>
@@ -60,4 +70,53 @@
</div> </div>
</div> </div>
<div id="unit-coordinates-container">
<div id="unit-coordinates" class="mouse-tool" data-location-system="LatLng">
<div class="mouse-tool-item" data-location-system="MGRS">
<div id="ref-unit-position-mgrs"></div>
<div id="unit-position-mgrs" class="coordinates copyable" data-location-system="MGRS"></div>
</div>
<div class="mouse-tool-item" data-location-system="LatLng">
<div id="ref-unit-position-latitude" data-location-system="LatLng"></div>
<div id="unit-position-latitude" class="coordinates copyable" data-location-system="LatLng"></div>
</div>
<div class="mouse-tool-item" data-location-system="LatLng">
<div id="ref-unit-position-longitude" data-location-system="LatLng"></div>
<div id="unit-position-longitude" class="coordinates copyable" data-location-system="LatLng"></div>
</div>
<div class="mouse-tool-item" data-location-system="LatLngDec">
<div id="ref-unit-position-latitude-dec" data-location-system="LatLngDec"></div>
<div id="unit-position-latitude-dec" class="coordinates copyable" data-location-system="LatLngDec"></div>
</div>
<div class="mouse-tool-item" data-location-system="LatLngDec">
<div id="ref-unit-position-longitude-dec" data-location-system="LatLngDec"></div>
<div id="unit-position-longitude-dec" class="coordinates copyable" data-location-system="LatLngDec"></div>
</div>
<div class="mouse-tool-item" data-location-system="UTM">
<div id="ref-unit-position-utm-northing" data-location-system="UTM"></div>
<div id="unit-position-utm-northing" class="coordinates copyable" data-location-system="UTM"></div>
</div>
<div class="mouse-tool-item" data-location-system="UTM">
<div id="ref-unit-position-utm-easting" data-location-system="UTM"></div>
<div id="unit-position-utm-easting" class="coordinates copyable" data-location-system="UTM"></div>
</div>
<div class="mouse-tool-item">
<div id="ref-unit-position-elevation" data-label="H"></div>
<div id="unit-position-elevation" class="elevation copyable" data-value="---"></div>
</div>
</div>
<div id="unit-coordinates-toggle" data-open="false">
Unit Coordinates <span id="unit-coordinates-toggle-icon"></span>
</div>
</div>
</div> </div>

View File

@@ -204,6 +204,16 @@ export class ControlTipsPlugin implements OlympusPlugin {
"key": `Period`, "key": `Period`,
"action": "Increase precision", "action": "Increase precision",
"mouseoverSelector": `#coordinates-tool[data-location-system="MGRS"], #coordinates-tool[data-location-system="MGRS"] *` "mouseoverSelector": `#coordinates-tool[data-location-system="MGRS"], #coordinates-tool[data-location-system="MGRS"] *`
},
{
"key": `Mouse1 or Z`,
"action": "Change location system",
"mouseoverSelector": "#unit-coordinates *"
},
{
"key": `Mouse2`,
"action": "Copy to clipboard",
"mouseoverSelector": `#unit-coordinates-container > #unit-coordinates > .mouse-tool-item > .copyable`
} }
] ]
}, },

View File

@@ -251,6 +251,7 @@ export const SHOW_UNIT_PATHS = "Show selected unit paths";
export const SHOW_UNIT_TARGETS = "Show selected unit targets"; export const SHOW_UNIT_TARGETS = "Show selected unit targets";
export const DCS_LINK_PORT = "DCS Camera link port"; export const DCS_LINK_PORT = "DCS Camera link port";
export const DCS_LINK_RATIO = "DCS Camera zoom"; export const DCS_LINK_RATIO = "DCS Camera zoom";
export const SHOW_HUMAN_CONTROLLED_UNIT_ORIGINAL_CALLSIGN = "Show human controlled unit original callsign";
export enum DataIndexes { export enum DataIndexes {
startOfData = 0, startOfData = 0,
@@ -262,6 +263,7 @@ export enum DataIndexes {
country, country,
name, name,
unitName, unitName,
callsign,
groupName, groupName,
state, state,
task, task,

View File

@@ -90,6 +90,7 @@ export interface ObjectIconOptions {
showAmmo: boolean, showAmmo: boolean,
showSummary: boolean, showSummary: boolean,
showCallsign: boolean, showCallsign: boolean,
showOriginalCallsign?: boolean,
rotateToHeading: boolean rotateToHeading: boolean
} }
@@ -144,6 +145,7 @@ export interface UnitData {
country: number; country: number;
name: string; name: string;
unitName: string; unitName: string;
callsign: string;
groupName: string; groupName: string;
state: string; state: string;
task: string; task: string;

View File

@@ -12,7 +12,7 @@ import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker"; import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap"; import { ClickableMiniMap } from "./clickableminimap";
import { SVGInjector } from '@tanem/svg-injector' import { SVGInjector } from '@tanem/svg-injector'
import { defaultMapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, MOVE_UNIT, SHOW_UNIT_CONTACTS, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, SHOW_UNIT_LABELS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, MAP_MARKER_CONTROLS, DCS_LINK_PORT, DCS_LINK_RATIO, defaultMapMirrors } from "../constants/constants"; import { defaultMapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, MOVE_UNIT, SHOW_UNIT_CONTACTS, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, SHOW_UNIT_LABELS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, MAP_MARKER_CONTROLS, DCS_LINK_PORT, DCS_LINK_RATIO, defaultMapMirrors, SHOW_HUMAN_CONTROLLED_UNIT_ORIGINAL_CALLSIGN } from "../constants/constants";
import { CoalitionArea } from "./coalitionarea/coalitionarea"; import { CoalitionArea } from "./coalitionarea/coalitionarea";
import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu"; import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";
import { DrawingCursor } from "./coalitionarea/drawingcursor"; import { DrawingCursor } from "./coalitionarea/drawingcursor";
@@ -152,6 +152,9 @@ export class Map extends L.Map {
/* Init the state machine */ /* Init the state machine */
this.#state = IDLE; this.#state = IDLE;
/* By default let's hide human controlled unit original callsign */
this.getContainer().toggleAttribute("data-hide-original-callsign", !this.getVisibilityOptions()[SHOW_HUMAN_CONTROLLED_UNIT_ORIGINAL_CALLSIGN]);
/* Register event handles */ /* Register event handles */
this.on("click", (e: any) => this.#onClick(e)); this.on("click", (e: any) => this.#onClick(e));
this.on("dblclick", (e: any) => this.#onDoubleClick(e)); this.on("dblclick", (e: any) => this.#onDoubleClick(e));
@@ -212,6 +215,7 @@ export class Map extends L.Map {
document.addEventListener("mapOptionsChanged", () => { document.addEventListener("mapOptionsChanged", () => {
this.getContainer().toggleAttribute("data-hide-labels", !this.getVisibilityOptions()[SHOW_UNIT_LABELS]); this.getContainer().toggleAttribute("data-hide-labels", !this.getVisibilityOptions()[SHOW_UNIT_LABELS]);
this.getContainer().toggleAttribute("data-hide-original-callsign", !this.getVisibilityOptions()[SHOW_HUMAN_CONTROLLED_UNIT_ORIGINAL_CALLSIGN]);
this.#cameraControlPort = this.getVisibilityOptions()[DCS_LINK_PORT] as number; this.#cameraControlPort = this.getVisibilityOptions()[DCS_LINK_PORT] as number;
this.#cameraZoomRatio = 50 / (20 + (this.getVisibilityOptions()[DCS_LINK_RATIO] as number)); this.#cameraZoomRatio = 50 / (20 + (this.getVisibilityOptions()[DCS_LINK_RATIO] as number));
@@ -303,6 +307,7 @@ export class Map extends L.Map {
this.addVisibilityOption(SHOW_UNITS_ENGAGEMENT_RINGS, true); this.addVisibilityOption(SHOW_UNITS_ENGAGEMENT_RINGS, true);
this.addVisibilityOption(SHOW_UNITS_ACQUISITION_RINGS, true); this.addVisibilityOption(SHOW_UNITS_ACQUISITION_RINGS, true);
this.addVisibilityOption(HIDE_UNITS_SHORT_RANGE_RINGS, true); this.addVisibilityOption(HIDE_UNITS_SHORT_RANGE_RINGS, true);
this.addVisibilityOption(SHOW_HUMAN_CONTROLLED_UNIT_ORIGINAL_CALLSIGN, false);
/* this.addVisibilityOption(FILL_SELECTED_RING, false); Removed since currently broken: TODO fix!*/ /* this.addVisibilityOption(FILL_SELECTED_RING, false); Removed since currently broken: TODO fix!*/
} }

View File

@@ -5,22 +5,26 @@ import { Unit } from "../unit/unit";
import { Panel } from "./panel"; import { Panel } from "./panel";
import formatcoords from "formatcoords"; import formatcoords from "formatcoords";
import { MGRS_PRECISION_100M, MGRS_PRECISION_10KM, MGRS_PRECISION_10M, MGRS_PRECISION_1KM, MGRS_PRECISION_1M } from "../constants/constants"; import { MGRS_PRECISION_100M, MGRS_PRECISION_10KM, MGRS_PRECISION_10M, MGRS_PRECISION_1KM, MGRS_PRECISION_1M } from "../constants/constants";
import { Popup } from "../popups/popup";
export class MouseInfoPanel extends Panel { export class MouseInfoPanel extends Panel {
#coordinatesElement:HTMLElement; #coordinatesElement: HTMLElement;
#locationSystems = [ "LatLng", "MGRS", "UTM" ]; #unitCoordinatesElement: HTMLElement;
#locationSystems = ["LatLng", "MGRS", "UTM", "LatLngDec"];
#measureMarker: Marker; #measureMarker: Marker;
#measurePoint: LatLng | null = null; #measurePoint: LatLng | null = null;
#measureIcon: Icon; #measureIcon: Icon;
#measureLine: Polyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false }); #measureLine: Polyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false });
#measureBox: HTMLElement; #measureBox: HTMLElement;
#MGRSPrecisions = [ MGRS_PRECISION_10KM, MGRS_PRECISION_1KM, MGRS_PRECISION_100M, MGRS_PRECISION_10M, MGRS_PRECISION_1M ]; #MGRSPrecisions = [MGRS_PRECISION_10KM, MGRS_PRECISION_1KM, MGRS_PRECISION_100M, MGRS_PRECISION_10M, MGRS_PRECISION_1M];
#selectedMGRSPrecisionIndex = 3; #selectedMGRSPrecisionIndex = 3;
#selectedLocationSystemIndex = 0; #selectedLocationSystemIndex = 0;
#elevationRequest: XMLHttpRequest | null = null; #elevationRequest: XMLHttpRequest | null = null;
#updateInterval: any = null;
constructor(ID: string) { constructor(ID: string) {
super( ID ); super(ID);
this.#measureIcon = new Icon({ iconUrl: 'resources/theme/images/icons/pin.svg', iconAnchor: [16, 32] }); this.#measureIcon = new Icon({ iconUrl: 'resources/theme/images/icons/pin.svg', iconAnchor: [16, 32] });
this.#measureMarker = new Marker([0, 0], { icon: this.#measureIcon, interactive: false }); this.#measureMarker = new Marker([0, 0], { icon: this.#measureIcon, interactive: false });
@@ -34,54 +38,132 @@ export class MouseInfoPanel extends Panel {
getApp().getMap()?.on('mousemove', (e: any) => this.#onMouseMove(e)); getApp().getMap()?.on('mousemove', (e: any) => this.#onMouseMove(e));
getApp().getMap()?.on('drag', (e: any) => this.#onMouseMove(e)); getApp().getMap()?.on('drag', (e: any) => this.#onMouseMove(e));
document.addEventListener('unitsSelection', (e: CustomEvent<Unit[]>) => this.#update()); document.addEventListener('unitsSelection', (e: CustomEvent<Unit[]>) => {
document.addEventListener('clearSelection', () => this.#update()); /* Let's update selected unit coordinates every second, useful for moving units */
this.#updateInterval = setInterval(() => {
var selectedUnits = getApp().getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1) {
this.#update()
}
}, 1000);
this.#update()
});
document.addEventListener('clearSelection', () => {
clearInterval(this.#updateInterval)
this.#update()
});
this.#coordinatesElement = <HTMLElement>this.getElement().querySelector( '#coordinates-tool' ); this.#coordinatesElement = <HTMLElement>this.getElement().querySelector('#coordinates-tool');
this.#coordinatesElement.addEventListener( "click", ( ev:MouseEvent ) => { this.#coordinatesElement.addEventListener("click", (ev: MouseEvent) => {
this.#changeLocationSystem(); this.#changeLocationSystem();
}); });
getApp().getShortcutManager().addKeyboardShortcut( "switchMapLocationSystem", { getApp().getShortcutManager().addKeyboardShortcut("switchMapLocationSystem", {
"callback": ( ev:KeyboardEvent ) => { "callback": (ev: KeyboardEvent) => {
this.#changeLocationSystem(); this.#changeLocationSystem();
}, },
"code": "KeyZ" "code": "KeyZ"
}).addKeyboardShortcut( "decreaseMGRSPrecision", { }).addKeyboardShortcut("decreaseMGRSPrecision", {
"callback": ( ev:KeyboardEvent ) => { "callback": (ev: KeyboardEvent) => {
if ( this.#getLocationSystem() !== "MGRS" ) { if (this.#getLocationSystem() !== "MGRS") {
return; return;
} }
if ( this.#selectedMGRSPrecisionIndex > 0 ) { if (this.#selectedMGRSPrecisionIndex > 0) {
this.#selectedMGRSPrecisionIndex--; this.#selectedMGRSPrecisionIndex--;
this.#update(); this.#update();
} }
}, },
"code": "Comma" "code": "Comma"
}).addKeyboardShortcut( "increaseMGRSPrecision", { }).addKeyboardShortcut("increaseMGRSPrecision", {
"callback": ( ev:KeyboardEvent ) => { "callback": (ev: KeyboardEvent) => {
if ( this.#getLocationSystem() !== "MGRS" ) { if (this.#getLocationSystem() !== "MGRS") {
return; return;
} }
if ( this.#selectedMGRSPrecisionIndex < this.#MGRSPrecisions.length - 1 ) { if (this.#selectedMGRSPrecisionIndex < this.#MGRSPrecisions.length - 1) {
this.#selectedMGRSPrecisionIndex++; this.#selectedMGRSPrecisionIndex++;
this.#update(); this.#update();
} }
}, },
"code": "Period" "code": "Period"
}); });
/* Selected unit coordinates panel interaction */
this.#unitCoordinatesElement = <HTMLElement>this.getElement().querySelector('#unit-coordinates');
this.#unitCoordinatesElement.addEventListener("click", (ev: MouseEvent) => {
this.#changeLocationSystem();
});
const unitCoordsToggleEl = <HTMLElement>this.getElement().querySelector('#unit-coordinates-toggle');
const unitCoordsContainer = <HTMLElement>this.getElement().querySelector('#unit-coordinates-container');
unitCoordsToggleEl.addEventListener("click", (ev: MouseEvent) => {
if (unitCoordsContainer.getAttribute('data-open') === 'true') {
unitCoordsContainer.setAttribute('data-open', 'false');
} else {
unitCoordsContainer.setAttribute('data-open', 'true');
}
});
/* Let's make coordinates copy-able */
this.#listenForCopyableElements();
}
#listenForCopyableElements() {
const copyableElements = document.getElementsByClassName('copyable');
for (const element of copyableElements) {
element.addEventListener('contextmenu', async (ev) => {
if (!ev.target) return;
const locationSystem = this.#getLocationSystem();
const coordinatesComposeElements = document.querySelectorAll(`.copyable[data-location-system=${locationSystem}]`)
const elevationComposeElement = document.querySelectorAll(`.copyable.elevation`)
let text = '';
for (const element of coordinatesComposeElements) {
text += (element as HTMLElement).innerText + '\n';
}
text += (elevationComposeElement[0] as HTMLElement).getAttribute('data-value');
if (!navigator || !navigator.clipboard) {
console.error('This browser does not support clipboard copy.');
return;
};
navigator.clipboard.writeText(text)
.then(() => {
(getApp().getPopupsManager().get("infoPopup") as Popup).setText('Coordinates copied to clipboard.');
})
.catch(err => {
(getApp().getPopupsManager().get("infoPopup") as Popup).setText('An error occurred while copying text to clipboard: ' + err);
});
});
}
} }
#update() { #update() {
const mousePosition = getApp().getMap().getMouseCoordinates(); const mousePosition = getApp().getMap().getMouseCoordinates();
var selectedUnitPosition = null; let selectedUnitPosition = null;
let selectedUnitElevation = null;
var selectedUnits = getApp().getUnitsManager().getSelectedUnits(); var selectedUnits = getApp().getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1) if (selectedUnits && selectedUnits.length == 1) {
this.getElement().querySelector(`#unit-coordinates-container`)?.classList.remove('hide');
selectedUnitPosition = new LatLng(selectedUnits[0].getPosition().lat, selectedUnits[0].getPosition().lng); selectedUnitPosition = new LatLng(selectedUnits[0].getPosition().lat, selectedUnits[0].getPosition().lng);
selectedUnitElevation = selectedUnits[0].getPosition().alt;
} else {
selectedUnitPosition = null;
selectedUnitElevation = null;
this.getElement().querySelector(`#unit-coordinates-container`)?.classList.add('hide');
}
/* Draw measures from selected unit, from pin location, and from bullseyes */ /* Draw measures from selected unit, from pin location, and from bullseyes */
this.#drawMeasure("ref-measure-position", "measure-position", this.#measurePoint, mousePosition); this.#drawMeasure("ref-measure-position", "measure-position", this.#measurePoint, mousePosition);
@@ -96,20 +178,61 @@ export class MouseInfoPanel extends Panel {
/* Draw coordinates */ /* Draw coordinates */
var coords = formatcoords(mousePosition.lat, mousePosition.lng); var coords = formatcoords(mousePosition.lat, mousePosition.lng);
var coordString = coords.format('XDDMMss', {decimalPlaces: 4}); var coordString = coords.format('XDDMMss', { decimalPlaces: 4 });
let decCoordsString = coords.format('XDDm', { decimalPlaces: 3 });
if ( this.#getLocationSystem() === "MGRS" ) { if (this.#getLocationSystem() === "MGRS") {
const mgrs = <MGRS>latLngToMGRS( mousePosition.lat, mousePosition.lng, this.#MGRSPrecisions[ this.#selectedMGRSPrecisionIndex ] ); const mgrs = <MGRS>latLngToMGRS(mousePosition.lat, mousePosition.lng, this.#MGRSPrecisions[this.#selectedMGRSPrecisionIndex]);
this.#drawCoordinates("ref-mouse-position-mgrs", "mouse-position-mgrs", "M"+mgrs.groups.join(" ") ); this.#drawCoordinates("ref-mouse-position-mgrs", "mouse-position-mgrs", "M" + mgrs.groups.join(" "));
} else if ( this.#getLocationSystem() === "UTM" ) { } else if (this.#getLocationSystem() === "UTM") {
const utm = latLngToUTM( mousePosition.lat, mousePosition.lng ); const utm = latLngToUTM(mousePosition.lat, mousePosition.lng);
this.#drawCoordinates("ref-mouse-position-utm-northing", "mouse-position-utm-northing", "N"+utm.northing); this.#drawCoordinates("ref-mouse-position-utm-northing", "mouse-position-utm-northing", "N" + utm.northing);
this.#drawCoordinates("ref-mouse-position-utm-easting", "mouse-position-utm-easting", "E"+utm.easting); this.#drawCoordinates("ref-mouse-position-utm-easting", "mouse-position-utm-easting", "E" + utm.easting);
} else { } else if (this.#getLocationSystem() === "LatLngDec") {
this.#drawCoordinates("ref-mouse-position-latitude-dec", "mouse-position-latitude-dec", decCoordsString.split(" ")[0] + "'");
this.#drawCoordinates("ref-mouse-position-longitude-dec", "mouse-position-longitude-dec", decCoordsString.split(" ")[1] + "'");
}
else {
this.#drawCoordinates("ref-mouse-position-latitude", "mouse-position-latitude", coordString.split(" ")[0]); this.#drawCoordinates("ref-mouse-position-latitude", "mouse-position-latitude", coordString.split(" ")[0]);
this.#drawCoordinates("ref-mouse-position-longitude", "mouse-position-longitude", coordString.split(" ")[1]); this.#drawCoordinates("ref-mouse-position-longitude", "mouse-position-longitude", coordString.split(" ")[1]);
} }
/* Draw selected unit coordinates */
if (selectedUnitPosition) {
var unitCoords = formatcoords(selectedUnitPosition.lat, selectedUnitPosition.lng);
var unitCoordString = unitCoords.format('XDDMMss', { decimalPlaces: 4 });
let decCoordsString = unitCoords.format('XDDm', { decimalPlaces: 3 });
if (this.#getLocationSystem() === "MGRS") {
const mgrs = <MGRS>latLngToMGRS(selectedUnitPosition.lat, selectedUnitPosition.lng, this.#MGRSPrecisions[this.#selectedMGRSPrecisionIndex]);
this.#drawCoordinates("ref-unit-position-mgrs", "unit-position-mgrs", "M" + mgrs.groups.join(" "));
} else if (this.#getLocationSystem() === "UTM") {
const utm = latLngToUTM(selectedUnitPosition.lat, selectedUnitPosition.lng);
this.#drawCoordinates("ref-unit-position-utm-northing", "unit-position-utm-northing", "N" + utm.northing);
this.#drawCoordinates("ref-unit-position-utm-easting", "unit-position-utm-easting", "E" + utm.easting);
} else if (this.#getLocationSystem() === "LatLngDec") {
this.#drawCoordinates("ref-unit-position-latitude-dec", "unit-position-latitude-dec", decCoordsString.split(" ")[0] + "'");
this.#drawCoordinates("ref-unit-position-longitude-dec", "unit-position-longitude-dec", decCoordsString.split(" ")[1] + "'");
} else {
this.#drawCoordinates("ref-unit-position-latitude", "unit-position-latitude", unitCoordString.split(" ")[0]);
this.#drawCoordinates("ref-unit-position-longitude", "unit-position-longitude", unitCoordString.split(" ")[1]);
}
const unitElevationElement = this.getElement().querySelector(`#unit-position-elevation`) as HTMLElement;
let displayedHeight = "0";
switch (selectedUnits[0].getCategory()) {
case 'Aircraft':
displayedHeight = "FL" + zeroAppend(Math.floor(mToFt(selectedUnitElevation as number) / 100), 3);
break;
default:
displayedHeight = Math.floor(mToFt(selectedUnitElevation as number)) + " ft"
break;
}
unitElevationElement.dataset.value = displayedHeight;
}
/* Get the ground elevation from the server endpoint */ /* Get the ground elevation from the server endpoint */
if (this.#elevationRequest == null) { if (this.#elevationRequest == null) {
this.#elevationRequest = new XMLHttpRequest(); this.#elevationRequest = new XMLHttpRequest();
@@ -128,9 +251,9 @@ export class MouseInfoPanel extends Panel {
} }
this.#elevationRequest = null; this.#elevationRequest = null;
}; };
this.#elevationRequest.ontimeout = () => {this.#elevationRequest = null;} this.#elevationRequest.ontimeout = () => { this.#elevationRequest = null; }
this.#elevationRequest.onerror = () => {this.#elevationRequest = null;} this.#elevationRequest.onerror = () => { this.#elevationRequest = null; }
this.#elevationRequest.onabort = () => {this.#elevationRequest = null;} this.#elevationRequest.onabort = () => { this.#elevationRequest = null; }
this.#elevationRequest.send(); this.#elevationRequest.send();
} }
} }
@@ -266,21 +389,21 @@ export class MouseInfoPanel extends Panel {
#changeLocationSystem() { #changeLocationSystem() {
if ( this.#selectedLocationSystemIndex < this.#locationSystems.length - 1 ) { if (this.#selectedLocationSystemIndex < this.#locationSystems.length - 1) {
this.#selectedLocationSystemIndex++; this.#selectedLocationSystemIndex++;
} else { } else {
this.#selectedLocationSystemIndex = 0; this.#selectedLocationSystemIndex = 0;
} }
this.#coordinatesElement.setAttribute( "data-location-system", this.#getLocationSystem() ); this.#coordinatesElement.setAttribute("data-location-system", this.#getLocationSystem());
this.#unitCoordinatesElement.setAttribute("data-location-system", this.#getLocationSystem());
this.#update(); this.#update();
} }
#getLocationSystem() { #getLocationSystem() {
const x = this.#locationSystems; const x = this.#locationSystems;
const y = this.#selectedLocationSystemIndex; const y = this.#selectedLocationSystemIndex;
const z = this.#locationSystems[ this.#selectedLocationSystemIndex ]; const z = this.#locationSystems[this.#selectedLocationSystemIndex];
return this.#locationSystems[this.#selectedLocationSystemIndex]; return this.#locationSystems[this.#selectedLocationSystemIndex];
} }
} }

View File

@@ -5,7 +5,7 @@ import { CustomMarker } from '../map/markers/custommarker';
import { SVGInjector } from '@tanem/svg-injector'; import { SVGInjector } from '@tanem/svg-injector';
import { UnitDatabase } from './databases/unitdatabase'; import { UnitDatabase } from './databases/unitdatabase';
import { TargetMarker } from '../map/markers/targetmarker'; import { TargetMarker } from '../map/markers/targetmarker';
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION, MAX_SHOTS_SCATTER, SHOTS_SCATTER_DEGREES, GROUND_UNIT_AIR_DEFENCE_REGEX } from '../constants/constants'; import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION, MAX_SHOTS_SCATTER, SHOTS_SCATTER_DEGREES, GROUND_UNIT_AIR_DEFENCE_REGEX, SHOW_HUMAN_CONTROLLED_UNIT_ORIGINAL_CALLSIGN } from '../constants/constants';
import { DataExtractor } from '../server/dataextractor'; import { DataExtractor } from '../server/dataextractor';
import { groundUnitDatabase } from './databases/groundunitdatabase'; import { groundUnitDatabase } from './databases/groundunitdatabase';
import { navyUnitDatabase } from './databases/navyunitdatabase'; import { navyUnitDatabase } from './databases/navyunitdatabase';
@@ -36,6 +36,7 @@ export abstract class Unit extends CustomMarker {
#country: number = 0; #country: number = 0;
#name: string = ""; #name: string = "";
#unitName: string = ""; #unitName: string = "";
#callsign: string = "";
#groupName: string = ""; #groupName: string = "";
#state: string = states[0]; #state: string = states[0];
#task: string = "" #task: string = ""
@@ -119,6 +120,7 @@ export abstract class Unit extends CustomMarker {
getCountry() { return this.#country }; getCountry() { return this.#country };
getName() { return this.#name }; getName() { return this.#name };
getUnitName() { return this.#unitName }; getUnitName() { return this.#unitName };
getCallsign() { return this.#callsign };
getGroupName() { return this.#groupName }; getGroupName() { return this.#groupName };
getState() { return this.#state }; getState() { return this.#state };
getTask() { return this.#task }; getTask() { return this.#task };
@@ -279,6 +281,7 @@ export abstract class Unit extends CustomMarker {
case DataIndexes.country: this.#country = dataExtractor.extractUInt8(); break; case DataIndexes.country: this.#country = dataExtractor.extractUInt8(); break;
case DataIndexes.name: this.#name = dataExtractor.extractString(); break; case DataIndexes.name: this.#name = dataExtractor.extractString(); break;
case DataIndexes.unitName: this.#unitName = dataExtractor.extractString(); break; case DataIndexes.unitName: this.#unitName = dataExtractor.extractString(); break;
case DataIndexes.callsign: this.#callsign = dataExtractor.extractString(); break;
case DataIndexes.groupName: this.#groupName = dataExtractor.extractString(); updateMarker = true; break; case DataIndexes.groupName: this.#groupName = dataExtractor.extractString(); updateMarker = true; break;
case DataIndexes.state: this.#state = enumToState(dataExtractor.extractUInt8()); updateMarker = true; break; case DataIndexes.state: this.#state = enumToState(dataExtractor.extractUInt8()); updateMarker = true; break;
case DataIndexes.task: this.#task = dataExtractor.extractString(); break; case DataIndexes.task: this.#task = dataExtractor.extractString(); break;
@@ -358,6 +361,7 @@ export abstract class Unit extends CustomMarker {
country: this.#country, country: this.#country,
name: this.#name, name: this.#name,
unitName: this.#unitName, unitName: this.#unitName,
callsign: this.#callsign,
groupName: this.#groupName, groupName: this.#groupName,
state: this.#state, state: this.#state,
task: this.#task, task: this.#task,
@@ -423,7 +427,7 @@ export abstract class Unit extends CustomMarker {
setSelected(selected: boolean) { setSelected(selected: boolean) {
/* Only alive units can be selected that belong to the commanded coalition can be selected */ /* Only alive units can be selected that belong to the commanded coalition can be selected */
if ((this.#alive || !selected) && this.belongsToCommandedCoalition() && this.getSelected() != selected) { if ((this.#alive || !selected) && this.belongsToCommandedCoalition() && this.getSelected() != selected) {
this.#selected = selected; this.#selected = selected;
/* If selected, update the marker to show the selected effects, else clear all the drawings that are only shown for selected units. */ /* If selected, update the marker to show the selected effects, else clear all the drawings that are only shown for selected units. */
if (selected) { if (selected) {
@@ -678,13 +682,20 @@ export abstract class Unit extends CustomMarker {
if (iconOptions.showSummary) { if (iconOptions.showSummary) {
var summary = document.createElement("div"); var summary = document.createElement("div");
summary.classList.add("unit-summary"); summary.classList.add("unit-summary");
var originalCallsign = document.createElement("div");
originalCallsign.classList.add("unit-original-callsign");
originalCallsign.innerText = this.#callsign;
var callsign = document.createElement("div"); var callsign = document.createElement("div");
callsign.classList.add("unit-callsign"); callsign.classList.add("unit-callsign");
callsign.innerText = this.#unitName; callsign.innerText = this.#unitName;
var altitude = document.createElement("div"); var altitude = document.createElement("div");
altitude.classList.add("unit-altitude"); altitude.classList.add("unit-altitude");
var speed = document.createElement("div"); var speed = document.createElement("div");
speed.classList.add("unit-speed"); speed.classList.add("unit-speed");
if (this.#human) summary.appendChild(originalCallsign);
if (iconOptions.showCallsign) summary.appendChild(callsign); if (iconOptions.showCallsign) summary.appendChild(callsign);
summary.appendChild(altitude); summary.appendChild(altitude);
summary.appendChild(speed); summary.appendChild(speed);
@@ -1485,6 +1496,7 @@ export abstract class Unit extends CustomMarker {
export abstract class AirUnit extends Unit { export abstract class AirUnit extends Unit {
getIconOptions() { getIconOptions() {
var belongsToCommandedCoalition = this.belongsToCommandedCoalition(); var belongsToCommandedCoalition = this.belongsToCommandedCoalition();
var showHumanControlledUnitGroup = getApp().getMap().getVisibilityOptions()[SHOW_HUMAN_CONTROLLED_UNIT_ORIGINAL_CALLSIGN] as boolean;
return { return {
showState: belongsToCommandedCoalition, showState: belongsToCommandedCoalition,
showVvi: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))), showVvi: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
@@ -1496,6 +1508,7 @@ export abstract class AirUnit extends Unit {
showAmmo: belongsToCommandedCoalition, showAmmo: belongsToCommandedCoalition,
showSummary: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))), showSummary: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
showCallsign: belongsToCommandedCoalition, showCallsign: belongsToCommandedCoalition,
showOriginalCallsign: showHumanControlledUnitGroup,
rotateToHeading: false rotateToHeading: false
}; };
} }

View File

@@ -1074,6 +1074,8 @@ function Olympus.setUnitsData(arg, time)
else else
table["unitName"] = unit:getName() table["unitName"] = unit:getName()
end end
-- In case of AI units the callSign and the unitName will be the same
table["callsign"] = unit:getName()
table["groupName"] = group:getName() table["groupName"] = group:getName()
table["isHuman"] = (unit:getPlayerName() ~= nil) table["isHuman"] = (unit:getPlayerName() ~= nil)
table["hasTask"] = controller:hasTask() table["hasTask"] = controller:hasTask()