mirror of
https://github.com/nasa/openmct.git
synced 2025-06-17 14:48:13 +00:00
Annotations for imagery prototype (#6624)
This commit is contained in:
@ -67,7 +67,6 @@ const config = {
|
|||||||
MCT: path.join(projectRootDir, 'src/MCT'),
|
MCT: path.join(projectRootDir, 'src/MCT'),
|
||||||
testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'),
|
testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'),
|
||||||
objectUtils: path.join(projectRootDir, 'src/api/objects/object-utils.js'),
|
objectUtils: path.join(projectRootDir, 'src/api/objects/object-utils.js'),
|
||||||
kdbush: path.join(projectRootDir, 'node_modules/kdbush/kdbush.min.js'),
|
|
||||||
utils: path.join(projectRootDir, 'src/utils')
|
utils: path.join(projectRootDir, 'src/utils')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -30,6 +30,7 @@ const { test, expect } = require('../../../../pluginFixtures');
|
|||||||
const { createDomainObjectWithDefaults } = require('../../../../appActions');
|
const { createDomainObjectWithDefaults } = require('../../../../appActions');
|
||||||
const backgroundImageSelector = '.c-imagery__main-image__background-image';
|
const backgroundImageSelector = '.c-imagery__main-image__background-image';
|
||||||
const panHotkey = process.platform === 'linux' ? ['Shift', 'Alt'] : ['Alt'];
|
const panHotkey = process.platform === 'linux' ? ['Shift', 'Alt'] : ['Alt'];
|
||||||
|
const tagHotkey = ['Shift', 'Alt'];
|
||||||
const expectedAltText = process.platform === 'linux' ? 'Shift+Alt drag to pan' : 'Alt drag to pan';
|
const expectedAltText = process.platform === 'linux' ? 'Shift+Alt drag to pan' : 'Alt drag to pan';
|
||||||
const thumbnailUrlParamsRegexp = /\?w=100&h=100/;
|
const thumbnailUrlParamsRegexp = /\?w=100&h=100/;
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
|
|
||||||
// Verify that the created object is focused
|
// Verify that the created object is focused
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(exampleImagery.name);
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText(exampleImagery.name);
|
||||||
await page.locator(backgroundImageSelector).hover({ trial: true });
|
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
|
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
|
||||||
@ -72,11 +73,11 @@ test.describe('Example Imagery Object', () => {
|
|||||||
test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
|
test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
|
||||||
const deltaYStep = 100; //equivalent to 1x zoom
|
const deltaYStep = 100; //equivalent to 1x zoom
|
||||||
|
|
||||||
await page.locator(backgroundImageSelector).hover({ trial: true });
|
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
||||||
|
|
||||||
// zoom in
|
// zoom in
|
||||||
await page.mouse.wheel(0, deltaYStep * 2);
|
await page.mouse.wheel(0, deltaYStep * 2);
|
||||||
await page.locator(backgroundImageSelector).hover({ trial: true });
|
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
||||||
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
||||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
||||||
@ -131,6 +132,36 @@ test.describe('Example Imagery Object', () => {
|
|||||||
expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
|
expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Can use alt+shift+drag to create a tag', async ({ page }) => {
|
||||||
|
const canvas = page.locator('canvas');
|
||||||
|
await canvas.hover({ trial: true });
|
||||||
|
|
||||||
|
const canvasBoundingBox = await canvas.boundingBox();
|
||||||
|
const canvasCenterX = canvasBoundingBox.x + canvasBoundingBox.width / 2;
|
||||||
|
const canvasCenterY = canvasBoundingBox.y + canvasBoundingBox.height / 2;
|
||||||
|
|
||||||
|
await Promise.all(tagHotkey.map((x) => page.keyboard.down(x)));
|
||||||
|
await page.mouse.down();
|
||||||
|
// steps not working for me here
|
||||||
|
await page.mouse.move(canvasCenterX - 20, canvasCenterY - 20);
|
||||||
|
await page.mouse.move(canvasCenterX - 100, canvasCenterY - 100);
|
||||||
|
await page.mouse.up();
|
||||||
|
await Promise.all(tagHotkey.map((x) => page.keyboard.up(x)));
|
||||||
|
|
||||||
|
//Wait for canvas to stablize.
|
||||||
|
await canvas.hover({ trial: true });
|
||||||
|
|
||||||
|
// add some tags
|
||||||
|
await page.getByText('Annotations').click();
|
||||||
|
await page.getByRole('button', { name: /Add Tag/ }).click();
|
||||||
|
await page.getByPlaceholder('Type to select tag').click();
|
||||||
|
await page.getByText('Driving').click();
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: /Add Tag/ }).click();
|
||||||
|
await page.getByPlaceholder('Type to select tag').click();
|
||||||
|
await page.getByText('Science').click();
|
||||||
|
});
|
||||||
|
|
||||||
test('Can use + - buttons to zoom on the image @unstable', async ({ page }) => {
|
test('Can use + - buttons to zoom on the image @unstable', async ({ page }) => {
|
||||||
await buttonZoomOnImageAndAssert(page);
|
await buttonZoomOnImageAndAssert(page);
|
||||||
});
|
});
|
||||||
@ -713,7 +744,6 @@ async function panZoomAndAssertImageProperties(page) {
|
|||||||
async function mouseZoomOnImageAndAssert(page, factor = 2) {
|
async function mouseZoomOnImageAndAssert(page, factor = 2) {
|
||||||
// Zoom in
|
// Zoom in
|
||||||
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
await page.locator(backgroundImageSelector).hover({ trial: true });
|
|
||||||
const deltaYStep = 100; // equivalent to 1x zoom
|
const deltaYStep = 100; // equivalent to 1x zoom
|
||||||
await page.mouse.wheel(0, deltaYStep * factor);
|
await page.mouse.wheel(0, deltaYStep * factor);
|
||||||
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
@ -724,7 +754,7 @@ async function mouseZoomOnImageAndAssert(page, factor = 2) {
|
|||||||
await page.mouse.move(imageCenterX, imageCenterY);
|
await page.mouse.move(imageCenterX, imageCenterY);
|
||||||
|
|
||||||
// Wait for zoom animation to finish
|
// Wait for zoom animation to finish
|
||||||
await page.locator(backgroundImageSelector).hover({ trial: true });
|
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
||||||
const imageMouseZoomed = await page.locator(backgroundImageSelector).boundingBox();
|
const imageMouseZoomed = await page.locator(backgroundImageSelector).boundingBox();
|
||||||
|
|
||||||
if (factor > 0) {
|
if (factor > 0) {
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"eslint-plugin-you-dont-need-lodash-underscore": "6.12.0",
|
"eslint-plugin-you-dont-need-lodash-underscore": "6.12.0",
|
||||||
"eventemitter3": "1.2.0",
|
"eventemitter3": "1.2.0",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
|
"flatbush": "4.1.0",
|
||||||
"git-rev-sync": "3.0.2",
|
"git-rev-sync": "3.0.2",
|
||||||
"html2canvas": "1.4.1",
|
"html2canvas": "1.4.1",
|
||||||
"imports-loader": "4.0.1",
|
"imports-loader": "4.0.1",
|
||||||
@ -44,7 +45,6 @@
|
|||||||
"karma-sourcemap-loader": "0.4.0",
|
"karma-sourcemap-loader": "0.4.0",
|
||||||
"karma-spec-reporter": "0.0.36",
|
"karma-spec-reporter": "0.0.36",
|
||||||
"karma-webpack": "5.0.0",
|
"karma-webpack": "5.0.0",
|
||||||
"kdbush": "3.0.0",
|
|
||||||
"location-bar": "3.0.1",
|
"location-bar": "3.0.1",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"mini-css-extract-plugin": "2.7.6",
|
"mini-css-extract-plugin": "2.7.6",
|
||||||
|
418
src/plugins/imagery/components/AnnotationsCanvas.vue
Normal file
418
src/plugins/imagery/components/AnnotationsCanvas.vue
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2023, 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<canvas
|
||||||
|
ref="canvas"
|
||||||
|
class="c-image-canvas"
|
||||||
|
style="width: 100%; height: 100%"
|
||||||
|
@mousedown="clearSelectedAnnotations"
|
||||||
|
@mousemove="trackAnnotationDrag"
|
||||||
|
@click="selectOrCreateAnnotation"
|
||||||
|
></canvas>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Flatbush from 'flatbush';
|
||||||
|
const EXISTING_ANNOTATION_STROKE_STYLE = '#D79078';
|
||||||
|
const EXISTING_ANNOTATION_FILL_STYLE = 'rgba(202, 202, 142, 0.2)';
|
||||||
|
const SELECTED_ANNOTATION_STROKE_COLOR = '#BD8ECC';
|
||||||
|
const SELECTED_ANNOTATION_FILL_STYLE = 'rgba(199, 87, 231, 0.2)';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'domainObject', 'objectPath'],
|
||||||
|
props: {
|
||||||
|
image: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
imageryAnnotations: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dragging: false,
|
||||||
|
mouseDown: false,
|
||||||
|
newAnnotationRectangle: {},
|
||||||
|
keyString: null,
|
||||||
|
context: null,
|
||||||
|
canvas: null,
|
||||||
|
selectedAnnotations: [],
|
||||||
|
indexToAnnotationMap: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
annotationsIndex() {
|
||||||
|
if (this.imageryAnnotations.length) {
|
||||||
|
// create a flatbush index for the annotations
|
||||||
|
const builtAnnotationsIndex = new Flatbush(this.imageryAnnotations.length);
|
||||||
|
this.imageryAnnotations.forEach((annotation) => {
|
||||||
|
const annotationRectangle = annotation.targets[this.keyString].rectangle;
|
||||||
|
const annotationRectangleForPixelDepth =
|
||||||
|
this.transformRectangleToPixelDense(annotationRectangle);
|
||||||
|
const indexNumber = builtAnnotationsIndex.add(
|
||||||
|
annotationRectangleForPixelDepth.x,
|
||||||
|
annotationRectangleForPixelDepth.y,
|
||||||
|
annotationRectangleForPixelDepth.x + annotationRectangleForPixelDepth.width,
|
||||||
|
annotationRectangleForPixelDepth.y + annotationRectangleForPixelDepth.height
|
||||||
|
);
|
||||||
|
this.indexToAnnotationMap[indexNumber] = annotation;
|
||||||
|
});
|
||||||
|
builtAnnotationsIndex.finish();
|
||||||
|
|
||||||
|
return builtAnnotationsIndex;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.canvas = this.$refs.canvas;
|
||||||
|
this.context = this.canvas.getContext('2d');
|
||||||
|
|
||||||
|
// adjust canvas size for retina displays
|
||||||
|
const pixelScale = window.devicePixelRatio;
|
||||||
|
this.canvas.width = Math.floor(this.canvas.width * pixelScale);
|
||||||
|
this.canvas.height = Math.floor(this.canvas.height * pixelScale);
|
||||||
|
|
||||||
|
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
this.openmct.selection.on('change', this.updateSelection);
|
||||||
|
this.drawAnnotations();
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.openmct.selection.off('change', this.updateSelection);
|
||||||
|
document.body.removeEventListener('click', this.cancelSelection);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onAnnotationChange(annotations) {
|
||||||
|
this.selectedAnnotations = annotations;
|
||||||
|
this.$emit('annotationsChanged', annotations);
|
||||||
|
},
|
||||||
|
updateSelection(selection) {
|
||||||
|
const selectionContext = selection?.[0]?.[0]?.context?.item;
|
||||||
|
const selectionType = selection?.[0]?.[0]?.context?.type;
|
||||||
|
const validSelectionTypes = ['clicked-on-image-selection'];
|
||||||
|
|
||||||
|
if (!validSelectionTypes.includes(selectionType)) {
|
||||||
|
// wrong type of selection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectionContext &&
|
||||||
|
this.openmct.objects.areIdsEqual(selectionContext.identifier, this.domainObject.identifier)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const incomingSelectedAnnotations = selection?.[0]?.[0]?.context?.annotations;
|
||||||
|
|
||||||
|
this.prepareExistingAnnotationSelection(incomingSelectedAnnotations);
|
||||||
|
},
|
||||||
|
prepareExistingAnnotationSelection(annotations) {
|
||||||
|
const targetDomainObjects = {};
|
||||||
|
targetDomainObjects[this.keyString] = this.domainObject;
|
||||||
|
|
||||||
|
const targetDetails = {};
|
||||||
|
annotations.forEach((annotation) => {
|
||||||
|
Object.entries(annotation.targets).forEach(([key, value]) => {
|
||||||
|
targetDetails[key] = value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.selectedAnnotations = annotations;
|
||||||
|
this.drawAnnotations();
|
||||||
|
|
||||||
|
return {
|
||||||
|
targetDomainObjects,
|
||||||
|
targetDetails
|
||||||
|
};
|
||||||
|
},
|
||||||
|
clearSelectedAnnotations() {
|
||||||
|
if (!this.openmct.annotation.getAvailableTags().length) {
|
||||||
|
// don't bother with new annotations if there are no tags
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mouseDown = true;
|
||||||
|
this.selectedAnnotations = [];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Given a rectangle, returns a rectangle that conforms to the pixel density of the device
|
||||||
|
* @param {Object} rectangle without pixel density applied
|
||||||
|
* @returns {Object} transformed rectangle with pixel density applied
|
||||||
|
*/
|
||||||
|
transformRectangleToPixelDense(rectangle) {
|
||||||
|
const pixelScale = window.devicePixelRatio;
|
||||||
|
const transformedRectangle = {
|
||||||
|
x: rectangle.x * pixelScale,
|
||||||
|
y: rectangle.y * pixelScale,
|
||||||
|
width: rectangle.width * pixelScale,
|
||||||
|
height: rectangle.height * pixelScale
|
||||||
|
};
|
||||||
|
return transformedRectangle;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Given a rectangle, returns a rectangle that is independent of the pixel density of the device
|
||||||
|
* @param {Object} rectangle with pixel density applied
|
||||||
|
* @returns {Object} transformed rectangle without pixel density applied
|
||||||
|
*/
|
||||||
|
transformRectangleFromPixelDense(rectangle) {
|
||||||
|
const pixelScale = window.devicePixelRatio;
|
||||||
|
const transformedRectangle = {
|
||||||
|
x: rectangle.x / pixelScale,
|
||||||
|
y: rectangle.y / pixelScale,
|
||||||
|
width: rectangle.width / pixelScale,
|
||||||
|
height: rectangle.height / pixelScale
|
||||||
|
};
|
||||||
|
return transformedRectangle;
|
||||||
|
},
|
||||||
|
drawRectInCanvas(rectangle, fillStyle, strokeStyle) {
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.lineWidth = 1;
|
||||||
|
this.context.fillStyle = fillStyle;
|
||||||
|
this.context.strokeStyle = strokeStyle;
|
||||||
|
this.context.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
|
||||||
|
this.context.fill();
|
||||||
|
this.context.stroke();
|
||||||
|
},
|
||||||
|
trackAnnotationDrag(event) {
|
||||||
|
if (this.mouseDown && !this.dragging && event.shiftKey && event.altKey) {
|
||||||
|
this.startAnnotationDrag(event);
|
||||||
|
} else if (this.dragging) {
|
||||||
|
const boundingRect = this.canvas.getBoundingClientRect();
|
||||||
|
const scaleX = this.canvas.width / boundingRect.width;
|
||||||
|
const scaleY = this.canvas.height / boundingRect.height;
|
||||||
|
this.newAnnotationRectangle = {
|
||||||
|
x: this.newAnnotationRectangle.x,
|
||||||
|
y: this.newAnnotationRectangle.y,
|
||||||
|
width: (event.clientX - boundingRect.left) * scaleX - this.newAnnotationRectangle.x,
|
||||||
|
height: (event.clientY - boundingRect.top) * scaleY - this.newAnnotationRectangle.y
|
||||||
|
};
|
||||||
|
this.drawAnnotations();
|
||||||
|
this.drawRectInCanvas(
|
||||||
|
this.newAnnotationRectangle,
|
||||||
|
SELECTED_ANNOTATION_FILL_STYLE,
|
||||||
|
SELECTED_ANNOTATION_STROKE_COLOR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearCanvas() {
|
||||||
|
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
},
|
||||||
|
selectImageView() {
|
||||||
|
// should show ImageView itself if we have no annotations to display
|
||||||
|
const selection = this.createPathSelection();
|
||||||
|
this.openmct.selection.select(selection, true);
|
||||||
|
},
|
||||||
|
createSelection(annotation) {
|
||||||
|
const selection = this.createPathSelection();
|
||||||
|
selection[0].context = annotation;
|
||||||
|
|
||||||
|
return selection;
|
||||||
|
},
|
||||||
|
selectImageAnnotations({ targetDetails, targetDomainObjects, annotations }) {
|
||||||
|
const annotationContext = {
|
||||||
|
type: 'clicked-on-image-selection',
|
||||||
|
targetDetails,
|
||||||
|
targetDomainObjects,
|
||||||
|
annotations,
|
||||||
|
annotationType: this.openmct.annotation.ANNOTATION_TYPES.PIXEL_SPATIAL,
|
||||||
|
onAnnotationChange: this.onAnnotationChange
|
||||||
|
};
|
||||||
|
const selection = this.createPathSelection();
|
||||||
|
if (
|
||||||
|
selection.length &&
|
||||||
|
this.openmct.objects.areIdsEqual(
|
||||||
|
selection[0].context.item.identifier,
|
||||||
|
this.domainObject.identifier
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
selection[0].context = {
|
||||||
|
...selection[0].context,
|
||||||
|
...annotationContext
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
selection.unshift({
|
||||||
|
element: this.$el,
|
||||||
|
context: {
|
||||||
|
item: this.domainObject,
|
||||||
|
...annotationContext
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openmct.selection.select(selection, true);
|
||||||
|
|
||||||
|
document.body.addEventListener('click', this.cancelSelection);
|
||||||
|
},
|
||||||
|
cancelSelection(event) {
|
||||||
|
if (this.$refs.canvas) {
|
||||||
|
const clickedInsideCanvas = this.$refs.canvas.contains(event.target);
|
||||||
|
const clickedInsideInspector = event.target.closest('.js-inspector') !== null;
|
||||||
|
const clickedOption = event.target.closest('.js-autocomplete-options') !== null;
|
||||||
|
if (!clickedInsideCanvas && !clickedInsideInspector && !clickedOption) {
|
||||||
|
this.newAnnotationRectangle = {};
|
||||||
|
this.selectedAnnotations = [];
|
||||||
|
this.drawAnnotations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createNewAnnotation() {
|
||||||
|
this.dragging = false;
|
||||||
|
this.selectedAnnotations = [];
|
||||||
|
|
||||||
|
const targetDomainObjects = {};
|
||||||
|
targetDomainObjects[this.keyString] = this.domainObject;
|
||||||
|
const targetDetails = {};
|
||||||
|
const rectangleFromCanvas = {
|
||||||
|
x: this.newAnnotationRectangle.x,
|
||||||
|
y: this.newAnnotationRectangle.y,
|
||||||
|
width: this.newAnnotationRectangle.width,
|
||||||
|
height: this.newAnnotationRectangle.height
|
||||||
|
};
|
||||||
|
const rectangleWithoutPixelScale = this.transformRectangleFromPixelDense(rectangleFromCanvas);
|
||||||
|
targetDetails[this.keyString] = {
|
||||||
|
rectangle: rectangleWithoutPixelScale,
|
||||||
|
time: this.image.time
|
||||||
|
};
|
||||||
|
this.selectImageAnnotations({
|
||||||
|
targetDetails,
|
||||||
|
targetDomainObjects,
|
||||||
|
annotations: []
|
||||||
|
});
|
||||||
|
},
|
||||||
|
attemptToSelectExistingAnnotation(event) {
|
||||||
|
this.dragging = false;
|
||||||
|
// use flatbush to find annotations that are close to the click
|
||||||
|
const boundingRect = this.canvas.getBoundingClientRect();
|
||||||
|
const scaleX = this.canvas.width / boundingRect.width;
|
||||||
|
const scaleY = this.canvas.height / boundingRect.height;
|
||||||
|
const x = (event.clientX - boundingRect.left) * scaleX;
|
||||||
|
const y = (event.clientY - boundingRect.top) * scaleY;
|
||||||
|
if (this.annotationsIndex) {
|
||||||
|
let nearbyAnnotations = [];
|
||||||
|
const resultIndicies = this.annotationsIndex.search(x, y, x, y);
|
||||||
|
resultIndicies.forEach((resultIndex) => {
|
||||||
|
const foundAnnotation = this.indexToAnnotationMap[resultIndex];
|
||||||
|
if (foundAnnotation._deleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nearbyAnnotations.push(foundAnnotation);
|
||||||
|
});
|
||||||
|
//show annotations if some were found
|
||||||
|
const { targetDomainObjects, targetDetails } =
|
||||||
|
this.prepareExistingAnnotationSelection(nearbyAnnotations);
|
||||||
|
this.selectImageAnnotations({
|
||||||
|
targetDetails,
|
||||||
|
targetDomainObjects,
|
||||||
|
annotations: nearbyAnnotations
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// nothing selected
|
||||||
|
this.drawAnnotations();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectOrCreateAnnotation(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.mouseDown = false;
|
||||||
|
if (
|
||||||
|
!this.dragging ||
|
||||||
|
(!this.newAnnotationRectangle.width && !this.newAnnotationRectangle.height)
|
||||||
|
) {
|
||||||
|
this.newAnnotationRectangle = {};
|
||||||
|
this.attemptToSelectExistingAnnotation(event);
|
||||||
|
} else {
|
||||||
|
this.createNewAnnotation();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createPathSelection() {
|
||||||
|
let selection = [];
|
||||||
|
selection.unshift({
|
||||||
|
element: this.$el,
|
||||||
|
context: {
|
||||||
|
item: this.domainObject
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.objectPath.forEach((pathObject, index) => {
|
||||||
|
selection.push({
|
||||||
|
element: this.openmct.layout.$refs.browseObject.$el,
|
||||||
|
context: {
|
||||||
|
item: pathObject
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return selection;
|
||||||
|
},
|
||||||
|
startAnnotationDrag(event) {
|
||||||
|
this.$emit('annotationMarqueed');
|
||||||
|
this.newAnnotationRectangle = {};
|
||||||
|
const boundingRect = this.canvas.getBoundingClientRect();
|
||||||
|
const scaleX = this.canvas.width / boundingRect.width;
|
||||||
|
const scaleY = this.canvas.height / boundingRect.height;
|
||||||
|
this.newAnnotationRectangle = {
|
||||||
|
x: (event.clientX - boundingRect.left) * scaleX,
|
||||||
|
y: (event.clientY - boundingRect.top) * scaleY
|
||||||
|
};
|
||||||
|
this.dragging = true;
|
||||||
|
},
|
||||||
|
isSelectedAnnotation(annotation) {
|
||||||
|
const someSelectedAnnotationExists = this.selectedAnnotations.some((selectedAnnotation) => {
|
||||||
|
return this.openmct.objects.areIdsEqual(
|
||||||
|
selectedAnnotation.identifier,
|
||||||
|
annotation.identifier
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return someSelectedAnnotationExists;
|
||||||
|
},
|
||||||
|
drawAnnotations() {
|
||||||
|
this.clearCanvas();
|
||||||
|
this.imageryAnnotations.forEach((annotation) => {
|
||||||
|
if (annotation._deleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rectangleForPixelDensity = this.transformRectangleToPixelDense(
|
||||||
|
annotation.targets[this.keyString].rectangle
|
||||||
|
);
|
||||||
|
if (this.isSelectedAnnotation(annotation)) {
|
||||||
|
this.drawRectInCanvas(
|
||||||
|
rectangleForPixelDensity,
|
||||||
|
SELECTED_ANNOTATION_FILL_STYLE,
|
||||||
|
SELECTED_ANNOTATION_STROKE_COLOR
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.drawRectInCanvas(
|
||||||
|
rectangleForPixelDensity,
|
||||||
|
EXISTING_ANNOTATION_FILL_STYLE,
|
||||||
|
EXISTING_ANNOTATION_STROKE_STYLE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -19,7 +19,7 @@ $elemBg: rgba(black, 0.7);
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 2;
|
z-index: 3;
|
||||||
@include userSelectNone;
|
@include userSelectNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,11 @@
|
|||||||
fetchpriority="low"
|
fetchpriority="low"
|
||||||
@load="imageLoadCompleted"
|
@load="imageLoadCompleted"
|
||||||
/>
|
/>
|
||||||
|
<i
|
||||||
|
v-show="showAnnotationIndicator"
|
||||||
|
class="c-thumb__annotation-indicator icon-status-poll-edit"
|
||||||
|
>
|
||||||
|
</i>
|
||||||
</a>
|
</a>
|
||||||
<div v-if="viewableArea" class="c-thumb__viewable-area" :style="viewableAreaStyle"></div>
|
<div v-if="viewableArea" class="c-thumb__viewable-area" :style="viewableAreaStyle"></div>
|
||||||
<div class="c-thumb__timestamp">{{ image.formattedTime }}</div>
|
<div class="c-thumb__timestamp">{{ image.formattedTime }}</div>
|
||||||
@ -66,6 +71,12 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
imageryAnnotations: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
viewableArea: {
|
viewableArea: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: function () {
|
default: function () {
|
||||||
@ -125,6 +136,11 @@ export default {
|
|||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
height: `${height}px`
|
height: `${height}px`
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
showAnnotationIndicator() {
|
||||||
|
return this.imageryAnnotations.some((annotation) => {
|
||||||
|
return !annotation._deleted;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -88,6 +88,13 @@
|
|||||||
:image="focusedImage"
|
:image="focusedImage"
|
||||||
:sized-image-dimensions="sizedImageDimensions"
|
:sized-image-dimensions="sizedImageDimensions"
|
||||||
/>
|
/>
|
||||||
|
<AnnotationsCanvas
|
||||||
|
v-if="shouldDisplayAnnotations"
|
||||||
|
:image="focusedImage"
|
||||||
|
:imagery-annotations="imageryAnnotations[focusedImage.time]"
|
||||||
|
@annotationMarqueed="handlePauseButton(true)"
|
||||||
|
@annotationsChanged="loadAnnotations"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -173,6 +180,7 @@
|
|||||||
:key="`${image.thumbnailUrl || image.url}-${image.time}-${index}`"
|
:key="`${image.thumbnailUrl || image.url}-${image.time}-${index}`"
|
||||||
:image="image"
|
:image="image"
|
||||||
:active="focusedImageIndex === index"
|
:active="focusedImageIndex === index"
|
||||||
|
:imagery-annotations="imageryAnnotations[image.time]"
|
||||||
:selected="focusedImageIndex === index && isPaused"
|
:selected="focusedImageIndex === index && isPaused"
|
||||||
:real-time="!isFixed"
|
:real-time="!isFixed"
|
||||||
:viewable-area="focusedImageIndex === index ? viewableArea : null"
|
:viewable-area="focusedImageIndex === index ? viewableArea : null"
|
||||||
@ -200,6 +208,7 @@ import Compass from './Compass/Compass.vue';
|
|||||||
import ImageControls from './ImageControls.vue';
|
import ImageControls from './ImageControls.vue';
|
||||||
import ImageThumbnail from './ImageThumbnail.vue';
|
import ImageThumbnail from './ImageThumbnail.vue';
|
||||||
import imageryData from '../../imagery/mixins/imageryData';
|
import imageryData from '../../imagery/mixins/imageryData';
|
||||||
|
import AnnotationsCanvas from './AnnotationsCanvas.vue';
|
||||||
|
|
||||||
const REFRESH_CSS_MS = 500;
|
const REFRESH_CSS_MS = 500;
|
||||||
const DURATION_TRACK_MS = 1000;
|
const DURATION_TRACK_MS = 1000;
|
||||||
@ -232,7 +241,8 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
Compass,
|
Compass,
|
||||||
ImageControls,
|
ImageControls,
|
||||||
ImageThumbnail
|
ImageThumbnail,
|
||||||
|
AnnotationsCanvas
|
||||||
},
|
},
|
||||||
mixins: [imageryData],
|
mixins: [imageryData],
|
||||||
inject: ['openmct', 'domainObject', 'objectPath', 'currentView', 'imageFreshnessOptions'],
|
inject: ['openmct', 'domainObject', 'objectPath', 'currentView', 'imageFreshnessOptions'],
|
||||||
@ -295,7 +305,8 @@ export default {
|
|||||||
animateZoom: true,
|
animateZoom: true,
|
||||||
imagePanned: false,
|
imagePanned: false,
|
||||||
forceShowThumbnails: false,
|
forceShowThumbnails: false,
|
||||||
animateThumbScroll: false
|
animateThumbScroll: false,
|
||||||
|
imageryAnnotations: {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -425,6 +436,19 @@ export default {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
shouldDisplayAnnotations() {
|
||||||
|
const imageHeightAndWidth = this.sizedImageHeight !== 0 && this.sizedImageWidth !== 0;
|
||||||
|
const display =
|
||||||
|
this.focusedImage !== undefined &&
|
||||||
|
this.focusedImageNaturalAspectRatio !== undefined &&
|
||||||
|
this.imageContainerWidth !== undefined &&
|
||||||
|
this.imageContainerHeight !== undefined &&
|
||||||
|
imageHeightAndWidth &&
|
||||||
|
this.zoomFactor === 1 &&
|
||||||
|
this.imagePanned !== true;
|
||||||
|
|
||||||
|
return display;
|
||||||
|
},
|
||||||
shouldDisplayCompass() {
|
shouldDisplayCompass() {
|
||||||
const imageHeightAndWidth = this.sizedImageHeight !== 0 && this.sizedImageWidth !== 0;
|
const imageHeightAndWidth = this.sizedImageHeight !== 0 && this.sizedImageWidth !== 0;
|
||||||
const display =
|
const display =
|
||||||
@ -631,6 +655,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.abortController = new AbortController();
|
||||||
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
eventHelpers.extend(this);
|
eventHelpers.extend(this);
|
||||||
this.focusedImageWrapper = this.$refs.focusedImageWrapper;
|
this.focusedImageWrapper = this.$refs.focusedImageWrapper;
|
||||||
@ -689,8 +716,12 @@ export default {
|
|||||||
|
|
||||||
this.listenTo(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
|
this.listenTo(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
|
||||||
this.loadVisibleLayers();
|
this.loadVisibleLayers();
|
||||||
|
this.loadAnnotations();
|
||||||
|
|
||||||
|
this.openmct.selection.on('change', this.updateSelection);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
this.abortController.abort();
|
||||||
this.persistVisibleLayers();
|
this.persistVisibleLayers();
|
||||||
this.stopFollowingTimeContext();
|
this.stopFollowingTimeContext();
|
||||||
|
|
||||||
@ -716,6 +747,15 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.stopListening(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
|
this.stopListening(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
|
||||||
|
|
||||||
|
Object.keys(this.imageryAnnotations).forEach((time) => {
|
||||||
|
const imageAnnotationsForTime = this.imageryAnnotations[time];
|
||||||
|
imageAnnotationsForTime.forEach((imageAnnotation) => {
|
||||||
|
this.openmct.objects.destroyMutable(imageAnnotation);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.openmct.selection.off('change', this.updateSelection);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
calculateViewHeight() {
|
calculateViewHeight() {
|
||||||
@ -743,6 +783,15 @@ export default {
|
|||||||
this.timeContext.off('clock', this.trackDuration);
|
this.timeContext.off('clock', this.trackDuration);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateSelection(selection) {
|
||||||
|
const selectionType = selection?.[0]?.[0]?.context?.type;
|
||||||
|
const validSelectionTypes = ['annotation-search-result'];
|
||||||
|
|
||||||
|
if (!validSelectionTypes.includes(selectionType)) {
|
||||||
|
// wrong type of selection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
expand() {
|
expand() {
|
||||||
// check for modifier keys so it doesnt interfere with the layout
|
// check for modifier keys so it doesnt interfere with the layout
|
||||||
if (this.cursorStates.modifierKeyPressed) {
|
if (this.cursorStates.modifierKeyPressed) {
|
||||||
@ -832,6 +881,41 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async loadAnnotations(existingAnnotations) {
|
||||||
|
if (!this.openmct.annotation.getAvailableTags().length) {
|
||||||
|
// don't bother loading annotations if there are no tags
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let foundAnnotations = existingAnnotations;
|
||||||
|
if (!foundAnnotations) {
|
||||||
|
// attempt to load
|
||||||
|
foundAnnotations = await this.openmct.annotation.getAnnotations(
|
||||||
|
this.domainObject.identifier,
|
||||||
|
this.abortController.signal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
foundAnnotations.forEach((foundAnnotation) => {
|
||||||
|
const targetId = Object.keys(foundAnnotation.targets)[0];
|
||||||
|
const timeForAnnotation = foundAnnotation.targets[targetId].time;
|
||||||
|
if (!this.imageryAnnotations[timeForAnnotation]) {
|
||||||
|
this.$set(this.imageryAnnotations, timeForAnnotation, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
const annotationExtant = this.imageryAnnotations[timeForAnnotation].some(
|
||||||
|
(existingAnnotation) => {
|
||||||
|
return this.openmct.objects.areIdsEqual(
|
||||||
|
existingAnnotation.identifier,
|
||||||
|
foundAnnotation.identifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!annotationExtant) {
|
||||||
|
const annotationArray = this.imageryAnnotations[timeForAnnotation];
|
||||||
|
const mutableAnnotation = this.openmct.objects.toMutable(foundAnnotation);
|
||||||
|
annotationArray.push(mutableAnnotation);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
persistVisibleLayers() {
|
persistVisibleLayers() {
|
||||||
if (
|
if (
|
||||||
this.domainObject.configuration &&
|
this.domainObject.configuration &&
|
||||||
@ -979,7 +1063,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Vue.nextTick();
|
await Vue.nextTick();
|
||||||
this.$refs.thumbsWrapper.scrollLeft = scrollWidth;
|
if (this.$refs.thumbsWrapper) {
|
||||||
|
this.$refs.thumbsWrapper.scrollLeft = scrollWidth;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scrollHandler() {
|
scrollHandler() {
|
||||||
if (this.isPaused) {
|
if (this.isPaused) {
|
||||||
|
@ -293,6 +293,13 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__annotation-indicator {
|
||||||
|
color: $colorClickIconButton;
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
&__timestamp {
|
&__timestamp {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
padding: 2px 3px;
|
padding: 2px 3px;
|
||||||
@ -540,3 +547,11 @@
|
|||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-image-canvas {
|
||||||
|
pointer-events: auto; // This allows the image element to receive a browser-level context click
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
@ -183,7 +183,7 @@ import MctTicks from './MctTicks.vue';
|
|||||||
import MctChart from './chart/MctChart.vue';
|
import MctChart from './chart/MctChart.vue';
|
||||||
import XAxis from './axis/XAxis.vue';
|
import XAxis from './axis/XAxis.vue';
|
||||||
import YAxis from './axis/YAxis.vue';
|
import YAxis from './axis/YAxis.vue';
|
||||||
import KDBush from 'kdbush';
|
import Flatbush from 'flatbush';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
const OFFSET_THRESHOLD = 10;
|
const OFFSET_THRESHOLD = 10;
|
||||||
@ -414,8 +414,8 @@ export default {
|
|||||||
// on clicking on a search result we highlight the annotation and zoom - we know it's an annotation result when isAnnotationSearchResult === true
|
// on clicking on a search result we highlight the annotation and zoom - we know it's an annotation result when isAnnotationSearchResult === true
|
||||||
// We shouldn't zoom when we're selecting existing annotations to view them or creating new annotations.
|
// We shouldn't zoom when we're selecting existing annotations to view them or creating new annotations.
|
||||||
const selectionType = selection?.[0]?.[0]?.context?.type;
|
const selectionType = selection?.[0]?.[0]?.context?.type;
|
||||||
const validSelectionTypes = ['clicked-on-plot-selection', 'plot-annotation-search-result'];
|
const validSelectionTypes = ['clicked-on-plot-selection', 'annotation-search-result'];
|
||||||
const isAnnotationSearchResult = selectionType === 'plot-annotation-search-result';
|
const isAnnotationSearchResult = selectionType === 'annotation-search-result';
|
||||||
|
|
||||||
if (!validSelectionTypes.includes(selectionType)) {
|
if (!validSelectionTypes.includes(selectionType)) {
|
||||||
// wrong type of selection
|
// wrong type of selection
|
||||||
@ -1398,6 +1398,24 @@ export default {
|
|||||||
|
|
||||||
return annotationsByPoints.flat();
|
return annotationsByPoints.flat();
|
||||||
},
|
},
|
||||||
|
searchWithFlatbush(seriesData, seriesModel, boundingBox) {
|
||||||
|
const flatbush = new Flatbush(seriesData.length);
|
||||||
|
seriesData.forEach((point) => {
|
||||||
|
const x = seriesModel.getXVal(point);
|
||||||
|
const y = seriesModel.getYVal(point);
|
||||||
|
flatbush.add(x, y, x, y);
|
||||||
|
});
|
||||||
|
flatbush.finish();
|
||||||
|
|
||||||
|
const rangeResults = flatbush.search(
|
||||||
|
boundingBox.minX,
|
||||||
|
boundingBox.minY,
|
||||||
|
boundingBox.maxX,
|
||||||
|
boundingBox.maxY
|
||||||
|
);
|
||||||
|
|
||||||
|
return rangeResults;
|
||||||
|
},
|
||||||
getPointsInBox(boundingBoxPerYAxis, rawAnnotation) {
|
getPointsInBox(boundingBoxPerYAxis, rawAnnotation) {
|
||||||
// load series models in KD-Trees
|
// load series models in KD-Trees
|
||||||
const seriesKDTrees = [];
|
const seriesKDTrees = [];
|
||||||
@ -1413,22 +1431,8 @@ export default {
|
|||||||
|
|
||||||
const seriesData = seriesModel.getSeriesData();
|
const seriesData = seriesModel.getSeriesData();
|
||||||
if (seriesData && seriesData.length) {
|
if (seriesData && seriesData.length) {
|
||||||
const kdTree = new KDBush(
|
|
||||||
seriesData,
|
|
||||||
(point) => {
|
|
||||||
return seriesModel.getXVal(point);
|
|
||||||
},
|
|
||||||
(point) => {
|
|
||||||
return seriesModel.getYVal(point);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const searchResults = [];
|
const searchResults = [];
|
||||||
const rangeResults = kdTree.range(
|
const rangeResults = this.searchWithFlatbush(seriesData, seriesModel, boundingBox);
|
||||||
boundingBox.minX,
|
|
||||||
boundingBox.minY,
|
|
||||||
boundingBox.maxX,
|
|
||||||
boundingBox.maxY
|
|
||||||
);
|
|
||||||
rangeResults.forEach((id) => {
|
rangeResults.forEach((id) => {
|
||||||
const seriesDatum = seriesData[id];
|
const seriesDatum = seriesData[id];
|
||||||
if (seriesDatum) {
|
if (seriesDatum) {
|
||||||
|
@ -122,11 +122,11 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.previewAction = new PreviewAction(this.openmct);
|
this.previewAction = new PreviewAction(this.openmct);
|
||||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||||
this.clickedPlotAnnotation = this.clickedPlotAnnotation.bind(this);
|
this.fireAnnotationSelection = this.fireAnnotationSelection.bind(this);
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.previewAction.off('isVisible', this.togglePreviewState);
|
this.previewAction.off('isVisible', this.togglePreviewState);
|
||||||
this.openmct.selection.off('change', this.clickedPlotAnnotation);
|
this.openmct.selection.off('change', this.fireAnnotationSelection);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clickedResult(event) {
|
clickedResult(event) {
|
||||||
@ -139,18 +139,15 @@ export default {
|
|||||||
if (!this.openmct.router.isNavigatedObject(objectPath)) {
|
if (!this.openmct.router.isNavigatedObject(objectPath)) {
|
||||||
// if we're not on the correct page, navigate to the object,
|
// if we're not on the correct page, navigate to the object,
|
||||||
// then wait for the selection event to fire before issuing a new selection
|
// then wait for the selection event to fire before issuing a new selection
|
||||||
if (
|
if (this.result.annotationType) {
|
||||||
this.result.annotationType === this.openmct.annotation.ANNOTATION_TYPES.PLOT_SPATIAL ||
|
this.openmct.selection.on('change', this.fireAnnotationSelection);
|
||||||
this.result.annotationType === this.openmct.annotation.ANNOTATION_TYPES.GEOSPATIAL
|
|
||||||
) {
|
|
||||||
this.openmct.selection.on('change', this.clickedPlotAnnotation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.openmct.router.navigate(resultUrl);
|
this.openmct.router.navigate(resultUrl);
|
||||||
} else {
|
} else {
|
||||||
// if this is the navigated object, then we are already on the correct page
|
// if this is the navigated object, then we are already on the correct page
|
||||||
// and just need to issue the selection event
|
// and just need to issue the selection event
|
||||||
this.clickedPlotAnnotation();
|
this.fireAnnotationSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -159,8 +156,8 @@ export default {
|
|||||||
this.previewAction.invoke(objectPath);
|
this.previewAction.invoke(objectPath);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clickedPlotAnnotation() {
|
fireAnnotationSelection() {
|
||||||
this.openmct.selection.off('change', this.clickedPlotAnnotation);
|
this.openmct.selection.off('change', this.fireAnnotationSelection);
|
||||||
|
|
||||||
const targetDetails = {};
|
const targetDetails = {};
|
||||||
const targetDomainObjects = {};
|
const targetDomainObjects = {};
|
||||||
@ -176,11 +173,11 @@ export default {
|
|||||||
element: this.$el,
|
element: this.$el,
|
||||||
context: {
|
context: {
|
||||||
item: this.result.targetModels[0],
|
item: this.result.targetModels[0],
|
||||||
type: 'plot-annotation-search-result',
|
type: 'annotation-search-result',
|
||||||
targetDetails,
|
targetDetails,
|
||||||
targetDomainObjects,
|
targetDomainObjects,
|
||||||
annotations: [this.result],
|
annotations: [this.result],
|
||||||
annotationType: this.openmct.annotation.ANNOTATION_TYPES.PLOT_SPATIAL,
|
annotationType: this.result.annotationType,
|
||||||
onAnnotationChange: () => {}
|
onAnnotationChange: () => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user