diff --git a/frontend/server/public/stylesheets/panels/mouseinfo.css b/frontend/server/public/stylesheets/panels/mouseinfo.css index c82b25e4..657a86b9 100644 --- a/frontend/server/public/stylesheets/panels/mouseinfo.css +++ b/frontend/server/public/stylesheets/panels/mouseinfo.css @@ -104,7 +104,76 @@ } #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="UTM"] [data-location-system="UTM"] { 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: "↓"; } \ No newline at end of file diff --git a/frontend/server/views/panels/mouseinfo.ejs b/frontend/server/views/panels/mouseinfo.ejs index 990258e7..78b96cbd 100644 --- a/frontend/server/views/panels/mouseinfo.ejs +++ b/frontend/server/views/panels/mouseinfo.ejs @@ -42,6 +42,16 @@
+
+
+
+
+ +
+
+
+
+
@@ -60,4 +70,53 @@
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ +
+ Unit Coordinates +
+
+ \ No newline at end of file diff --git a/frontend/website/plugins/controltips/src/controltipsplugin.ts b/frontend/website/plugins/controltips/src/controltipsplugin.ts index 4d2bfe4d..2c1850da 100644 --- a/frontend/website/plugins/controltips/src/controltipsplugin.ts +++ b/frontend/website/plugins/controltips/src/controltipsplugin.ts @@ -204,6 +204,16 @@ export class ControlTipsPlugin implements OlympusPlugin { "key": `Period`, "action": "Increase precision", "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` } ] }, diff --git a/frontend/website/src/panels/mouseinfopanel.ts b/frontend/website/src/panels/mouseinfopanel.ts index 82dba41e..dc71a5a0 100644 --- a/frontend/website/src/panels/mouseinfopanel.ts +++ b/frontend/website/src/panels/mouseinfopanel.ts @@ -5,10 +5,12 @@ import { Unit } from "../unit/unit"; import { Panel } from "./panel"; import formatcoords from "formatcoords"; import { MGRS_PRECISION_100M, MGRS_PRECISION_10KM, MGRS_PRECISION_10M, MGRS_PRECISION_1KM, MGRS_PRECISION_1M } from "../constants/constants"; +import { log } from "console"; export class MouseInfoPanel extends Panel { #coordinatesElement:HTMLElement; - #locationSystems = [ "LatLng", "MGRS", "UTM" ]; + #unitCoordinatesElement:HTMLElement; + #locationSystems = [ "LatLng", "MGRS", "UTM", "LatLngDec" ]; #measureMarker: Marker; #measurePoint: LatLng | null = null; #measureIcon: Icon; @@ -18,6 +20,8 @@ export class MouseInfoPanel extends Panel { #selectedMGRSPrecisionIndex = 3; #selectedLocationSystemIndex = 0; #elevationRequest: XMLHttpRequest | null = null; + #updateInterval: any = null; + constructor(ID: string) { super( ID ); @@ -34,8 +38,20 @@ export class MouseInfoPanel extends Panel { getApp().getMap()?.on('mousemove', (e: any) => this.#onMouseMove(e)); getApp().getMap()?.on('drag', (e: any) => this.#onMouseMove(e)); - document.addEventListener('unitsSelection', (e: CustomEvent) => this.#update()); - document.addEventListener('clearSelection', () => this.#update()); + document.addEventListener('unitsSelection', (e: CustomEvent) => { + /* 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 = this.getElement().querySelector( '#coordinates-tool' ); @@ -73,15 +89,82 @@ export class MouseInfoPanel extends Panel { }, "code": "Period" }); + + /* Selected unit coordinates panel interaction */ + this.#unitCoordinatesElement = this.getElement().querySelector( '#unit-coordinates' ); + + this.#unitCoordinatesElement.addEventListener( "click", ( ev:MouseEvent ) => { + this.#changeLocationSystem(); + }); + + const unitCoordsToggleEl = this.getElement().querySelector('#unit-coordinates-toggle'); + const unitCoordsContainer = 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'); + + // TODO: copy all the coordinates and elevation instead of copying only lat, lng or elevation. + + for (const element of copyableElements) { + element.addEventListener('contextmenu', (ev) => { + if (!ev.target) return; + + const el = ev.target as HTMLElement; + + let text = null; + + if (el.innerText !== "") { + text = el.innerText; + } + + if (el.getAttribute('data-value')) { + text = el.getAttribute('data-value'); + } + + if (!text) return; + + if(!navigator || !navigator.clipboard) return; + + navigator.clipboard.writeText(text) + .then(() => { + // Text copied to clipboard. TODO: add a toast or alert for this event. + }) + .catch(err => { + console.error('An error occurred while copying text to clipboard: ', err); + }); + }); + } } #update() { const mousePosition = getApp().getMap().getMouseCoordinates(); - var selectedUnitPosition = null; + let selectedUnitPosition = null; + let selectedUnitElevation = null; + 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); + 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 */ this.#drawMeasure("ref-measure-position", "measure-position", this.#measurePoint, mousePosition); @@ -97,6 +180,7 @@ export class MouseInfoPanel extends Panel { /* Draw coordinates */ var coords = formatcoords(mousePosition.lat, mousePosition.lng); var coordString = coords.format('XDDMMss', {decimalPlaces: 4}); + let decCoordsString = coords.format('XDDm', {decimalPlaces: 3}); if ( this.#getLocationSystem() === "MGRS" ) { const mgrs = latLngToMGRS( mousePosition.lat, mousePosition.lng, this.#MGRSPrecisions[ this.#selectedMGRSPrecisionIndex ] ); @@ -105,11 +189,51 @@ export class MouseInfoPanel extends Panel { 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-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-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 = 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 */ if (this.#elevationRequest == null) { this.#elevationRequest = new XMLHttpRequest(); @@ -273,10 +397,10 @@ export class MouseInfoPanel extends Panel { } this.#coordinatesElement.setAttribute( "data-location-system", this.#getLocationSystem() ); + this.#unitCoordinatesElement.setAttribute( "data-location-system", this.#getLocationSystem() ); this.#update(); } - #getLocationSystem() { const x = this.#locationSystems; const y = this.#selectedLocationSystemIndex;