Skip to content

Commit

Permalink
Spectrogram Legend (#24)
Browse files Browse the repository at this point in the history
* adding legend capabilities

* add hover state and improve legend

* toggle icon bar

* defaulting grid to off

* fix for NaN numbers in annotations not in compressed view
  • Loading branch information
BryonLewis authored Jan 30, 2024
1 parent b506734 commit 06a04d2
Show file tree
Hide file tree
Showing 7 changed files with 498 additions and 32 deletions.
91 changes: 72 additions & 19 deletions client/src/components/SpectrogramViewer.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script lang="ts">
import { defineComponent, PropType, ref, Ref, watch } from "vue";
import { SpectroInfo, useGeoJS } from './geoJS/geoJSUtils';
import { SpectroInfo, useGeoJS } from "./geoJS/geoJSUtils";
import { patchAnnotation, putAnnotation, SpectrogramAnnotation } from "../api/api";
import LayerManager from "./geoJS/LayerManager.vue";
import { GeoEvent } from "geojs";
import geo from "geojs";
export default defineComponent({
name: "SpectroViewer",
components: {
LayerManager
LayerManager,
},
props: {
image: {
Expand All @@ -23,46 +25,97 @@ export default defineComponent({
default: () => [],
},
selectedId: {
type: Number as PropType<number | null>,
default: null,
type: Number as PropType<number | null>,
default: null,
},
recordingId: {
type: String as PropType<string | null>,
required: true,
}
},
grid: {
type: Boolean,
default: false,
},
},
emits: ['update:annotation', 'create:annotation', 'selected', 'geoViewerRef'],
emits: ["update:annotation", "create:annotation", "selected", "geoViewerRef", "hoverData"],
setup(props, { emit }) {
const containerRef: Ref<HTMLElement | undefined> = ref();
const geoJS = useGeoJS();
const initialized = ref(false);
const initialized = ref(false);
const mouseMoveEvent = (e: GeoEvent) => {
const { x, y } = e.geo;
if (!props.spectroInfo) {
return;
}
const freq =
props.spectroInfo.height - y >= 0 ?
(((props.spectroInfo.height - y) * (props.spectroInfo.high_freq - props.spectroInfo.low_freq)) / props.spectroInfo.height) / 1000 + props.spectroInfo.low_freq / 1000 : -1;
if (!props.spectroInfo.end_times && !props.spectroInfo.start_times) {
if (x >= 0 && props.spectroInfo.height - y >= 0) {
const time =
x *
((props.spectroInfo.end_time - props.spectroInfo.start_time) / props.spectroInfo.width);
emit("hoverData", { time, freq });
} else {
emit("hoverData", { time: -1, freq: -1 });
}
} else if (props.spectroInfo && props.spectroInfo.start_times && props.spectroInfo.end_times) { // compressed view
if (x >= 0 && props.spectroInfo.height - y >= 0) {
const timeLength = props.spectroInfo.end_time - props.spectroInfo.start_time;
const timeToPixels = props.spectroInfo.width / timeLength;
// find X in the range
let offsetAdditive = 0;
for (let i =0; i < props.spectroInfo.start_times.length; i += 1) {
const start_time = props.spectroInfo.start_times[i];
const end_time = props.spectroInfo.end_times[i];
const startX = offsetAdditive;
const endX = offsetAdditive + ((end_time - start_time) * timeToPixels);
if (x > startX && x < endX ) {
const timeOffset = x - offsetAdditive;
const time = start_time + (timeOffset / timeToPixels);
emit("hoverData", { time, freq });
return;
}
offsetAdditive += (end_time - start_time) * timeToPixels;
}
} else {
emit("hoverData", { time: -1, freq: -1 });
}
}
};
watch(containerRef, () => {
const { naturalWidth, naturalHeight } = props.image;
if (containerRef.value)
geoJS.initializeViewer(containerRef.value, naturalWidth, naturalHeight);
geoJS.initializeViewer(containerRef.value, naturalWidth, naturalHeight);
geoJS.drawImage(props.image, naturalWidth, naturalHeight);
initialized.value = true;
emit('geoViewerRef', geoJS.getGeoViewer());
emit("geoViewerRef", geoJS.getGeoViewer());
geoJS.getGeoViewer().value.geoOn(geo.event.mousemove, mouseMoveEvent);
});
const updateAnnotation = async (annotation: SpectrogramAnnotation) => {
// We call the patch on the selected annotation
if (props.recordingId !== null && props.selectedId !== null) {
await patchAnnotation(props.recordingId, props.selectedId, annotation);
emit('update:annotation', annotation);
emit("update:annotation", annotation);
}
};
const createAnnotation = async (annotation: SpectrogramAnnotation) => {
// We call the patch on the selected annotation
if (props.recordingId !== null) {
const response = await putAnnotation(props.recordingId, annotation);
emit('create:annotation', response.data.id);
const response = await putAnnotation(props.recordingId, annotation);
emit("create:annotation", response.data.id);
}
};
return {
containerRef,
geoViewerRef: geoJS.getGeoViewer(),
Expand All @@ -87,7 +140,8 @@ export default defineComponent({
:spectro-info="spectroInfo"
:annotations="annotations"
:selected-id="selectedId"
@selected="$emit('selected',$event)"
:grid="grid"
@selected="$emit('selected', $event)"
@update:annotation="updateAnnotation($event)"
@create:annotation="createAnnotation($event)"
/>
Expand All @@ -108,16 +162,14 @@ export default defineComponent({
display: flex;
flex-direction: column;
.geojs-map {
margin:2px;
margin: 2px;
&.geojs-map:focus {
outline: none;
}
}
}
.playback-container {
flex: 1;
}
.loadingSpinnerContainer {
z-index: 20;
Expand All @@ -131,4 +183,5 @@ export default defineComponent({
.geojs-map.annotation-input {
cursor: inherit;
}
}</style>
}
</style>
1 change: 1 addition & 0 deletions client/src/components/ThumbnailViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export default defineComponent({
:spectro-info="spectroInfo"
:annotations="annotations"
:selected-id="selectedId"
thumbnail
@selected="$emit('selected',$event)"
/>
</div>
Expand Down
23 changes: 23 additions & 0 deletions client/src/components/geoJS/LayerManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SpectrogramAnnotation } from "../../api/api";
import { geojsonToSpectro, SpectroInfo } from "./geoJSUtils";
import EditAnnotationLayer from "./layers/editAnnotationLayer";
import RectangleLayer from "./layers/rectangleLayer";
import LegendLayer from "./layers/legendLayer";
import { cloneDeep } from "lodash";
import useState from "../../use/useState";
export default defineComponent({
Expand All @@ -26,6 +27,14 @@ export default defineComponent({
type: Number as PropType<number | null>,
default: null,
},
thumbnail: {
type: Boolean,
default: false,
},
grid: {
type: Boolean,
default: false,
}
},
emits: ['selected', 'update:annotation', 'create:annotation'],
setup(props, { emit }) {
Expand All @@ -37,6 +46,7 @@ export default defineComponent({
const editingAnnotation: Ref<null | SpectrogramAnnotation> = ref(null);
let rectAnnotationLayer: RectangleLayer;
let editAnnotationLayer: EditAnnotationLayer;
let legendLayer: LegendLayer;
const displayError = ref(false);
const errorMsg = ref('');
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -161,6 +171,9 @@ export default defineComponent({
rectAnnotationLayer.formatData(localAnnotations.value, selectedAnnotationId.value);
rectAnnotationLayer.redraw();
}
if (!props.thumbnail) {
legendLayer.redraw();
}
if (editing.value && editingAnnotation.value) {
setTimeout(() => {
editAnnotationLayer.changeData(editingAnnotation.value);
Expand All @@ -185,6 +198,16 @@ export default defineComponent({
editAnnotationLayer = new EditAnnotationLayer(props.geoViewerRef, event, props.spectroInfo);
rectAnnotationLayer.formatData(localAnnotations.value, selectedAnnotationId.value);
rectAnnotationLayer.redraw();
if (!props.thumbnail) {
legendLayer = new LegendLayer(props.geoViewerRef, event, props.spectroInfo);
legendLayer.redraw();
}
}
});
watch(() => props.grid, () => {
if (!props.thumbnail && legendLayer) {
legendLayer.setGridEnabled(props.grid);
triggerUpdate();
}
});
watch(
Expand Down
17 changes: 11 additions & 6 deletions client/src/components/geoJS/geoJSUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,12 @@ const useGeoJS = () => {
const { width: mapWidth } = geoViewer.value.camera().viewport;

const bounds = !thumbnail.value
? { left: 0, top: 0, right: mapWidth, bottom: originalBounds.bottom }
? {
left: -125, // Making sure the legend is on the screen
top: 0,
right: mapWidth,
bottom: originalBounds.bottom,
}
: originalBounds;
const zoomAndCenter = geoViewer.value.zoomAndCenterFromBounds(bounds, 0);
geoViewer.value.zoom(zoomAndCenter.zoom);
Expand Down Expand Up @@ -255,11 +260,11 @@ function spectroToGeoJSon(
type: "Polygon",
coordinates: [
[
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[-1, -1],
[-1, -1],
[-1, -1],
[-1, -1],
[-1, -1],
],
],
};
Expand Down
Loading

0 comments on commit 06a04d2

Please sign in to comment.