Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions client/src/components/LayerManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default defineComponent({
selected: selectedTrackIdRef,
stateStyling,
};
const toolTipWidget = uiLayer.addDOMWidget('customToolTip', ToolTipWidget, toolTipWidgetProps);
uiLayer.addDOMWidget('customToolTip', ToolTipWidget, toolTipWidgetProps, { x: 10, y: 10 });

function updateLayers(
frame: number,
Expand All @@ -121,9 +121,12 @@ export default defineComponent({
const currentFrameIds: TrackId[] = intervalTree
.search([frame, frame])
.map((str: string) => parseInt(str, 10));

rectAnnotationLayer.setHoverAnnotations(visibleModes.includes('tooltip'));
polyAnnotationLayer.setHoverAnnotations(visibleModes.includes('tooltip'));
const inlcudesTooltip = visibleModes.includes('tooltip');
rectAnnotationLayer.setHoverAnnotations(inlcudesTooltip);
polyAnnotationLayer.setHoverAnnotations(inlcudesTooltip);
if (!inlcudesTooltip) {
hoverOvered.value = [];
}
const frameData = [] as FrameDataTrack[];
const editingTracks = [] as FrameDataTrack[];
currentFrameIds.forEach(
Expand Down Expand Up @@ -316,14 +319,35 @@ export default defineComponent({
});
editAnnotationLayer.bus.$on('update:selectedIndex',
(index: number, _type: EditAnnotationTypes, key = '') => handler.selectFeatureHandle(index, key));
const annotationHoverTooltip = (found: { trackType: [string, number]; trackId: number}[],
pos: {x: number; y: number}) => {
hoverOvered.value = found.map((item) => ({
type: item.trackType[0],
confidence: item.trackType[1],
trackId: item.trackId,
}));
toolTipWidget.position(pos);
const annotationHoverTooltip = (
found: {
trackType: [string, number];
trackId: number;
polygon: { coordinates: Array<Array<[number, number]>>};
}[],
) => {
const hoveredVals: (ToolTipWidgetData & { maxX: number})[] = [];
found.forEach((item) => {
// get Max of X and Min of y for ordering
if (item.polygon.coordinates.length) {
let maxX = -Infinity;
let minY = Infinity;
item.polygon.coordinates[0].forEach((coord) => {
if (coord.length === 2) {
maxX = Math.max(coord[0], maxX);
minY = Math.min(coord[1], minY);
}
});
hoveredVals.push({
type: item.trackType[0],
confidence: item.trackType[1],
trackId: item.trackId,
maxX,
});
}
});
hoverOvered.value = hoveredVals.sort((a, b) => a.maxX - b.maxX);
uiLayer.setToolTipWidget('customToolTip', (hoverOvered.value.length > 0));
};
rectAnnotationLayer.bus.$on('annotation-hover', annotationHoverTooltip);
polyAnnotationLayer.bus.$on('annotation-hover', annotationHoverTooltip);
Expand Down
1 change: 0 additions & 1 deletion client/src/layers/UILayers/ToolTipWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export default defineComponent({
<v-card
v-if="dataList.value.length"
dark
max-width="200px"
class="d-inline-flex pa-2"
>
<div>
Expand Down
65 changes: 59 additions & 6 deletions client/src/layers/UILayers/UILayer.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { createApp } from '@vue/composition-api';
import geo, { GeoEvent } from 'geojs';
import { MediaController } from '../../components/annotators/mediaControllerType';


interface WidgetPosition {
x: string | number;
y: string | number;
x: number;
y: number;
}

export interface DOMWidget {
canvas: () => HTMLElement;
isInViewport: () => boolean;
position: (pos: WidgetPosition) => WidgetPosition;
position: (pos: WidgetPosition) => void;
toolTip: boolean; // Tooltip enabled or disabled
toolTipOffset: {x: number; y: number}; // Offset from Cursor Position
lastMousePos: WidgetPosition; //Last Mouse position for zooming adjustments
}


/**
* UILayer provides a way to add Reactive VUE DOM Widgets to GeoJS
* These widgets are created under their own Vue App and can't rely on parent elements
Expand All @@ -31,18 +38,64 @@ export default class UILayer {
this.annotator = annotator;
this.widgets = {};
this.uiLayer = this.annotator.geoViewerRef.value.createLayer('ui');
this.uiLayer.geoOn(geo.event.mousemove, this.updateToolTipPositions);
this.uiLayer.geoOn(geo.event.zoom, this.zoomToolTipPosition);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
addDOMWidget(name: string, component: any, props: any, position = { x: 0, y: 0 }) {
updateWidgetToolTipPosition(mousePos: WidgetPosition, widget: DOMWidget) {
const tipOffset = widget.toolTipOffset;
const newOffset = this.uiLayer.map().gcsToDisplay(mousePos);
const finalOffset = this.uiLayer.map().displayToGcs(
{
x: newOffset.x + tipOffset.x, y: newOffset.y + tipOffset.y,
},
);
widget.position(finalOffset);
}

zoomToolTipPosition = () => {
Object.keys(this.widgets).forEach((name) => {
if (this.widgets[name].toolTip) {
const mousePos = this.widgets[name].lastMousePos;
this.updateWidgetToolTipPosition(mousePos, this.widgets[name]);
}
});
};

updateToolTipPositions = (evt: GeoEvent) => {
const mousePos = evt.geo;
Object.keys(this.widgets).forEach((name) => {
if (this.widgets[name].toolTip) {
this.updateWidgetToolTipPosition(mousePos, this.widgets[name]);
this.widgets[name].lastMousePos = mousePos;
}
});
};


addDOMWidget(
name: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
component: any, props: any,
position = { x: 0, y: 0 },
) {
const widget: DOMWidget = this.uiLayer.createWidget('dom', { position });
widget.canvas().setAttribute('id', name);
const parent = widget.canvas();
const div = document.createElement('div');
const element = parent.appendChild(div);
createApp(component, props).mount(element);
widget.toolTipOffset = position;
widget.toolTip = false;
widget.lastMousePos = position;
this.widgets[name] = widget;

return widget;
}

// Toggle named widget toolTip On or Off
setToolTipWidget(name: string, on: boolean) {
if (this.widgets[name]) {
this.widgets[name].toolTip = on;
}
}
}