diff --git a/src/plugins/imagery/ImageryViewProvider.js b/src/plugins/imagery/ImageryViewProvider.js
index aeb7555d77..1419990027 100644
--- a/src/plugins/imagery/ImageryViewProvider.js
+++ b/src/plugins/imagery/ImageryViewProvider.js
@@ -1,3 +1,25 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
import ImageryViewLayout from './components/ImageryViewLayout.vue';
import Vue from 'vue';
diff --git a/src/plugins/imagery/components/Compass/Compass.vue b/src/plugins/imagery/components/Compass/Compass.vue
new file mode 100644
index 0000000000..9080b38b84
--- /dev/null
+++ b/src/plugins/imagery/components/Compass/Compass.vue
@@ -0,0 +1,131 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+
+
+
+
+
+
+
+
diff --git a/src/plugins/imagery/components/Compass/CompassHUD.vue b/src/plugins/imagery/components/Compass/CompassHUD.vue
new file mode 100644
index 0000000000..b3a66e090f
--- /dev/null
+++ b/src/plugins/imagery/components/Compass/CompassHUD.vue
@@ -0,0 +1,145 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+
+
+
+ {{ point.direction }}
+
+
+
+
+
+
+
diff --git a/src/plugins/imagery/components/Compass/CompassRose.vue b/src/plugins/imagery/components/Compass/CompassRose.vue
new file mode 100644
index 0000000000..3a09db17c9
--- /dev/null
+++ b/src/plugins/imagery/components/Compass/CompassRose.vue
@@ -0,0 +1,263 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/plugins/imagery/components/Compass/compass.scss b/src/plugins/imagery/components/Compass/compass.scss
new file mode 100644
index 0000000000..424d0b2fca
--- /dev/null
+++ b/src/plugins/imagery/components/Compass/compass.scss
@@ -0,0 +1,214 @@
+/***************************** THEME/UI CONSTANTS AND MIXINS */
+$interfaceKeyColor: #00B9C5;
+$elemBg: rgba(black, 0.7);
+
+@mixin sun($position: 'circle closest-side') {
+ $color: #ff9900;
+ $gradEdgePerc: 60%;
+ background: radial-gradient(#{$position}, $color, $color $gradEdgePerc, rgba($color, 0.4) $gradEdgePerc + 5%, transparent);
+
+}
+
+.c-compass {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 1;
+ @include userSelectNone;
+}
+
+/***************************** COMPASS HUD */
+.c-hud {
+ // To be placed within a imagery view, in the bounding box of the image
+ $m: 1px;
+ $padTB: 2px;
+ $padLR: $padTB;
+ color: $interfaceKeyColor;
+ font-size: 0.8em;
+ position: absolute;
+ top: $m; right: $m; left: $m;
+ height: 18px;
+
+ svg, div {
+ position: absolute;
+ }
+
+ &__display {
+ height: 30px;
+ pointer-events: all;
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ }
+
+ &__range {
+ border: 1px solid $interfaceKeyColor;
+ border-top-color: transparent;
+ position: absolute;
+ top: 50%; right: $padLR; bottom: $padTB; left: $padLR;
+ }
+
+ [class*="__dir"] {
+ // NSEW
+ display: inline-block;
+ font-weight: bold;
+ text-shadow: 0 1px 2px black;
+ top: 50%;
+ transform: translate(-50%,-50%);
+ z-index: 2;
+ }
+
+ [class*="__dir--sub"] {
+ font-weight: normal;
+ opacity: 0.5;
+ }
+
+ &__sun {
+ $s: 10px;
+ @include sun('circle farthest-side at bottom');
+ bottom: $padTB + 2px;
+ height: $s; width: $s*2;
+ opacity: 0.8;
+ transform: translateX(-50%);
+ z-index: 1;
+ }
+
+}
+
+/***************************** COMPASS DIRECTIONS */
+.c-nsew {
+ $color: $interfaceKeyColor;
+ $inset: 7%;
+ $tickHeightPerc: 15%;
+ text-shadow: black 0 0 10px;
+ top: $inset; right: $inset; bottom: $inset; left: $inset;
+ z-index: 3;
+
+ &__tick,
+ &__label {
+ fill: $color;
+ }
+
+ &__minor-ticks {
+ opacity: 0.5;
+ transform-origin: center;
+ transform: rotate(45deg);
+ }
+
+ &__label {
+ dominant-baseline: central;
+ font-size: 0.8em;
+ font-weight: bold;
+ }
+
+ .c-label-n {
+ font-size: 1.1em;
+ }
+}
+
+/***************************** CAMERA FIELD ANGLE */
+.c-cam-field {
+ $color: white;
+ opacity: 0.2;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 2;
+
+ .cam-field-half {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+
+ .cam-field-area {
+ background: $color;
+ top: -30%;
+ right: 0;
+ bottom: -30%;
+ left: 0;
+ }
+
+ // clip-paths overlap a bit to avoid a gap between halves
+ &-l {
+ clip-path: polygon(0 0, 50.5% 0, 50.5% 100%, 0 100%);
+ .cam-field-area {
+ transform-origin: left center;
+ }
+ }
+
+ &-r {
+ clip-path: polygon(49.5% 0, 100% 0, 100% 100%, 49.5% 100%);
+ .cam-field-area {
+ transform-origin: right center;
+ }
+ }
+ }
+}
+
+/***************************** SPACECRAFT BODY */
+.c-spacecraft-body {
+ $color: $interfaceKeyColor;
+ $s: 30%;
+ background: $color;
+ border-radius: 3px;
+ height: $s; width: $s;
+ left: 50%; top: 50%;
+ opacity: 0.4;
+ transform-origin: center top;
+
+ &:before {
+ // Direction arrow
+ $color: rgba(black, 0.5);
+ $arwPointerY: 60%;
+ $arwBodyOffset: 25%;
+ background: $color;
+ content: '';
+ display: block;
+ position: absolute;
+ top: 10%; right: 20%; bottom: 50%; left: 20%;
+ clip-path: polygon(50% 0, 100% $arwPointerY, 100%-$arwBodyOffset $arwPointerY, 100%-$arwBodyOffset 100%, $arwBodyOffset 100%, $arwBodyOffset $arwPointerY, 0 $arwPointerY);
+ }
+}
+
+/***************************** DIRECTION ROSE */
+.c-direction-rose {
+ $d: 100px;
+ $c2: rgba(white, 0.1);
+ background: $elemBg;
+ background-image: radial-gradient(circle closest-side, transparent, transparent 80%, $c2);
+ width: $d;
+ height: $d;
+ transform-origin: 0 0;
+ position: absolute;
+ bottom: 10px; left: 10px;
+ clip-path: circle(50% at 50% 50%);
+ border-radius: 100%;
+
+ svg, div {
+ position: absolute;
+ }
+
+ // Sun
+ .c-sun {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+
+ &:before {
+ $s: 35%;
+ @include sun();
+ content: '';
+ display: block;
+ position: absolute;
+ opacity: 0.7;
+ top: 0; left: 50%;
+ height:$s; width: $s;
+ transform: translate(-50%, -60%);
+ }
+ }
+}
diff --git a/src/plugins/imagery/components/Compass/pluginSpec.js b/src/plugins/imagery/components/Compass/pluginSpec.js
new file mode 100644
index 0000000000..72b7d53b4d
--- /dev/null
+++ b/src/plugins/imagery/components/Compass/pluginSpec.js
@@ -0,0 +1,84 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2020, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import Compass from './Compass.vue';
+import Vue from 'vue';
+
+const COMPASS_ROSE_CLASS = '.c-direction-rose';
+const COMPASS_HUD_CLASS = '.c-compass__hud';
+
+describe("The Compass component", () => {
+ let app;
+ let instance;
+
+ beforeEach(() => {
+ let imageDatum = {
+ heading: 100,
+ roll: 90,
+ pitch: 90,
+ cameraTilt: 100,
+ cameraPan: 90,
+ sunAngle: 30
+ };
+ let propsData = {
+ containerWidth: 600,
+ containerHeight: 600,
+ naturalAspectRatio: 0.9,
+ image: imageDatum
+ };
+
+ app = new Vue({
+ components: { Compass },
+ data() {
+ return propsData;
+ },
+ template: ``
+ });
+ instance = app.$mount();
+
+ });
+
+ afterAll(() => {
+ app.$destroy();
+ });
+
+ describe("when a heading value exists on the image", () => {
+
+ it("should display a compass rose", () => {
+ let compassRoseElement = instance.$el.querySelector(COMPASS_ROSE_CLASS
+ );
+
+ expect(compassRoseElement).toBeDefined();
+ });
+
+ it("should display a compass HUD", () => {
+ let compassHUDElement = instance.$el.querySelector(COMPASS_HUD_CLASS);
+
+ expect(compassHUDElement).toBeDefined();
+ });
+
+ });
+
+});
diff --git a/src/plugins/imagery/components/Compass/utils.js b/src/plugins/imagery/components/Compass/utils.js
new file mode 100644
index 0000000000..67e82f5db8
--- /dev/null
+++ b/src/plugins/imagery/components/Compass/utils.js
@@ -0,0 +1,33 @@
+export function rotate(direction, ...rotations) {
+ const rotation = rotations.reduce((a, b) => a + b, 0);
+
+ return normalizeCompassDirection(direction + rotation);
+}
+
+export function normalizeCompassDirection(degrees) {
+ const base = degrees % 360;
+
+ return base >= 0 ? base : 360 + base;
+}
+
+export function inRange(degrees, [min, max]) {
+ return min > max
+ ? (degrees >= min && degrees < 360) || (degrees <= max && degrees >= 0)
+ : degrees >= min && degrees <= max;
+}
+
+export function percentOfRange(degrees, [min, max]) {
+ let distance = degrees;
+ let minRange = min;
+ let maxRange = max;
+
+ if (min > max) {
+ if (distance < max) {
+ distance += 360;
+ }
+
+ maxRange += 360;
+ }
+
+ return (distance - minRange) / (maxRange - minRange);
+}
diff --git a/src/plugins/imagery/components/ImageryViewLayout.vue b/src/plugins/imagery/components/ImageryViewLayout.vue
index 3891af88b0..15a2d59ab0 100644
--- a/src/plugins/imagery/components/ImageryViewLayout.vue
+++ b/src/plugins/imagery/components/ImageryViewLayout.vue
@@ -1,3 +1,25 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+