Skip to content

Commit

Permalink
adding in a compressedOverlay for visualizing compressed items in the…
Browse files Browse the repository at this point in the history
… main view
  • Loading branch information
BryonLewis committed Nov 11, 2024
1 parent e3ab5f4 commit cecc903
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 6 deletions.
8 changes: 8 additions & 0 deletions bats_ai/core/views/recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ def get_spectrogram(request: HttpRequest, id: int):

with colormap(None):
spectrogram = recording.spectrogram

compressed = recording.compressed_spectrogram

spectro_data = {
'url': spectrogram.image_url,
Expand All @@ -290,6 +292,12 @@ def get_spectrogram(request: HttpRequest, id: int):
'high_freq': spectrogram.frequency_max,
},
}
if compressed:
spectro_data['compressed'] = {
'start_times': compressed.starts,
'end_times': compressed.stops,
}

# Get distinct other users who have made annotations on the recording
if recording.owner == request.user:
other_users_qs = (
Expand Down
4 changes: 4 additions & 0 deletions client/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ export interface Spectrogram {
annotations?: SpectrogramAnnotation[];
temporal?: SpectrogramTemporalAnnotation[];
spectroInfo?: SpectroInfo;
compressed?: {
start_times: number[];
end_times: number[];
}
currentUser?: string;
otherUsers?: UserInfo[];

Expand Down
22 changes: 22 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, SpectrogramTemporalAnnotation } from "../../api/
import { geojsonToSpectro, SpectroInfo } from "./geoJSUtils";
import EditAnnotationLayer from "./layers/editAnnotationLayer";
import RectangleLayer from "./layers/rectangleLayer";
import CompressedOverlayLayer from "./layers/compressedOverlayLayer";
import TemporalLayer from "./layers/temporalLayer";
import LegendLayer from "./layers/legendLayer";
import TimeLayer from "./layers/timeLayer";
Expand Down Expand Up @@ -65,6 +66,7 @@ export default defineComponent({
const editing = ref(false);
const editingAnnotation: Ref<null | SpectrogramAnnotation | SpectrogramTemporalAnnotation> = ref(null);
let rectAnnotationLayer: RectangleLayer;
let compressedOverlayLayer: CompressedOverlayLayer;
let temporalAnnotationLayer: TemporalLayer;
let editAnnotationLayer: EditAnnotationLayer;
let legendLayer: LegendLayer;
Expand Down Expand Up @@ -315,6 +317,10 @@ export default defineComponent({
);
rectAnnotationLayer.redraw();
}
if (compressedOverlayLayer && !props.spectroInfo?.compressedWidth && props.spectroInfo?.start_times && props.spectroInfo.end_times) {
compressedOverlayLayer.formatData(props.spectroInfo.start_times, props.spectroInfo.end_times, props.yScale);
compressedOverlayLayer.redraw();
}
if (temporalAnnotationLayer && layerVisibility.value.includes('temporal')) {
temporalAnnotationLayer.formatData(
temporalAnnotations,
Expand Down Expand Up @@ -409,6 +415,9 @@ export default defineComponent({
if (rectAnnotationLayer) {
rectAnnotationLayer.destroy();
}
if (compressedOverlayLayer) {
compressedOverlayLayer.destroy();
}
if (temporalAnnotationLayer) {
temporalAnnotationLayer.destroy();
}
Expand Down Expand Up @@ -437,13 +446,22 @@ export default defineComponent({
} else {
rectAnnotationLayer.spectroInfo = props.spectroInfo;
}
if (!compressedOverlayLayer) {
compressedOverlayLayer = new CompressedOverlayLayer(props.geoViewerRef, props.spectroInfo);
} else {
compressedOverlayLayer.spectroInfo = props.spectroInfo;
}
if (!temporalAnnotationLayer) {
temporalAnnotationLayer = new TemporalLayer(props.geoViewerRef, temporalEvent, props.spectroInfo);
} {
temporalAnnotationLayer.spectroInfo = props.spectroInfo;
}
rectAnnotationLayer.formatData(localAnnotations.value, selectedAnnotationId.value, currentUser.value, colorScale.value, props.yScale);
rectAnnotationLayer.redraw();
if (compressedOverlayLayer && props.spectroInfo.start_times && props.spectroInfo.end_times) {
compressedOverlayLayer.formatData(props.spectroInfo.start_times, props.spectroInfo.end_times, props.yScale);
compressedOverlayLayer.redraw();
}
temporalAnnotationLayer.formatData(localTemporalAnnotations.value, selectedAnnotationId.value, currentUser.value, colorScale.value, props.yScale);
temporalAnnotationLayer.redraw();
if (!props.thumbnail) {
Expand Down Expand Up @@ -514,6 +532,10 @@ export default defineComponent({
props.yScale,
);
rectAnnotationLayer.redraw();
if (compressedOverlayLayer && props.spectroInfo?.start_times && props.spectroInfo.end_times) {
compressedOverlayLayer.setScaledDimensions(props.scaledWidth, props.scaledHeight);
compressedOverlayLayer.formatData(props.spectroInfo.start_times, props.spectroInfo.end_times, props.yScale);
}
editAnnotationLayer.setScaledDimensions(props.scaledWidth, props.scaledHeight);
if (editing.value && editingAnnotation.value) {
setTimeout(() => {
Expand Down
8 changes: 4 additions & 4 deletions client/src/components/geoJS/geoJSUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ function spectroTemporalToGeoJSon(
const adjustedWidth = scaledWidth > spectroInfo.width ? scaledWidth : spectroInfo.width;
// const adjustedHeight = scaledHeight > spectroInfo.height ? scaledHeight : spectroInfo.height;
//scale pixels to time and frequency ranges
if (spectroInfo.start_times === undefined || spectroInfo.end_times === undefined) {
if (spectroInfo.compressedWidth && spectroInfo.start_times === undefined || spectroInfo.end_times === undefined) {
const widthScale = adjustedWidth / (spectroInfo.end_time - spectroInfo.start_time);
// Now we remap our annotation to pixel coordinates
const start_time = annotation.start_time * widthScale;
Expand Down Expand Up @@ -374,7 +374,7 @@ function spectroToGeoJSon(
const adjustedWidth = scaledWidth > spectroInfo.width ? scaledWidth : spectroInfo.width;
const adjustedHeight = scaledHeight > spectroInfo.height ? scaledHeight : spectroInfo.height;

if (spectroInfo.start_times === undefined || spectroInfo.end_times === undefined) {
if (spectroInfo.compressedWidth === undefined) {
const widthScale = adjustedWidth / (spectroInfo.end_time - spectroInfo.start_time);
const heightScale = adjustedHeight / (spectroInfo.high_freq - spectroInfo.low_freq);
// Now we remap our annotation to pixel coordinates
Expand All @@ -396,7 +396,7 @@ function spectroToGeoJSon(
],
],
};
} else if (spectroInfo.start_times && spectroInfo.end_times) {
} else if (spectroInfo.compressedWidth && spectroInfo.start_times && spectroInfo.end_times) {
// Compressed Spectro has different conversion
// Find what section the annotation is in
const start = annotation.start_time;
Expand Down Expand Up @@ -504,7 +504,7 @@ function geojsonToSpectro(
const adjustedHeight = scaledHeight > spectroInfo.height ? scaledHeight : spectroInfo.height;

const coords = geojson.geometry.coordinates[0];
if (spectroInfo.start_times === undefined && spectroInfo.end_times === undefined) {
if (spectroInfo.compressedWidth === undefined) {
const widthScale = adjustedWidth / (spectroInfo.end_time - spectroInfo.start_time);
const heightScale = adjustedHeight / (spectroInfo.high_freq - spectroInfo.low_freq);
const start_time = Math.round(coords[1][0] / widthScale);
Expand Down
139 changes: 139 additions & 0 deletions client/src/components/geoJS/layers/compressedOverlayLayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* eslint-disable class-methods-use-this */
import { SpectroInfo } from "../geoJSUtils";
import { LayerStyle } from "./types";

interface RectCompressedGeoJSData {
polygon: GeoJSON.Polygon;
}


function scaleCompressedTime(start_time: number, end_time: number, spectroInfo: SpectroInfo, yScale: number, scaledWidth: number, scaledHeight: number) {
const adjustedWidth = scaledWidth > spectroInfo.width ? scaledWidth : spectroInfo.width;
const adjustedHeight = scaledHeight > spectroInfo.height ? scaledHeight : spectroInfo.height;

const widthScale = adjustedWidth / (spectroInfo.end_time - spectroInfo.start_time);
const heightScale = adjustedHeight / (spectroInfo.high_freq - spectroInfo.low_freq);
// Now we remap our annotation to pixel coordinates
const low_freq =
adjustedHeight - (spectroInfo.low_freq) * heightScale;
const high_freq =
adjustedHeight - (spectroInfo.high_freq) * heightScale;
const start_time_scaled = start_time * widthScale;
const end_time_scaled = end_time * widthScale;
return {
type: "Polygon",
coordinates: [
[
[start_time_scaled, low_freq * yScale],
[start_time_scaled, high_freq * yScale],
[end_time_scaled, high_freq * yScale],
[end_time_scaled, low_freq * yScale],
[start_time_scaled, low_freq * yScale],
],
],
} as GeoJSON.Polygon;

}

export default class CompressedOverlayLayer {
formattedData: RectCompressedGeoJSData[];


// eslint-disable-next-line @typescript-eslint/no-explicit-any
featureLayer: any;


// eslint-disable-next-line @typescript-eslint/no-explicit-any
geoViewerRef: any;

spectroInfo: SpectroInfo;

style: LayerStyle<RectCompressedGeoJSData>;

scaledWidth: number;
scaledHeight: number;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
geoViewerRef: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
spectroInfo: SpectroInfo
) {
this.geoViewerRef = geoViewerRef;
this.spectroInfo = spectroInfo;
this.formattedData = [];
this.scaledWidth = 0;
this.scaledHeight = 0;
//Only initialize once, prevents recreating Layer each edit
const layer = this.geoViewerRef.createLayer("feature", {
features: ["polygon"],
});
this.featureLayer = layer
.createFeature("polygon", { selectionAPI: true });
this.style = this.createStyle();
}

setScaledDimensions(newWidth: number, newHeight: number) {
this.scaledWidth = newWidth;
this.scaledHeight = newHeight;
}

destroy() {
if (this.featureLayer) {
this.geoViewerRef.deleteLayer(this.featureLayer);
}
}

formatData(
startTimes: number[],
endTimes: number[],
yScale = 1,
) {
const arr: RectCompressedGeoJSData[] = [];

for (let i = 0; i< startTimes.length; i += 1) {
const startTime = startTimes[i];
const endTime = endTimes[i];
const polygon = scaleCompressedTime(startTime, endTime, this.spectroInfo, yScale, this.scaledWidth, this.scaledHeight);
const newAnnotation: RectCompressedGeoJSData = {
polygon,
};
arr.push(newAnnotation);
}
this.formattedData = arr;
}

redraw() {
// add some styles
this.featureLayer
.data(this.formattedData)
.polygon((d: RectCompressedGeoJSData) => d.polygon.coordinates[0])
.style(this.createStyle())
.draw();
}

disable() {
this.featureLayer.data([]).draw();
}

createStyle(): LayerStyle<RectCompressedGeoJSData> {
return {
...{
strokeColor: "black",
strokeWidth: 2.0,
antialiasing: 0,
stroke: true,
uniformPolygon: true,
fill: false,
},
// Style conversion to get array objects to work in geoJS
position: (point) => {
return { x: point[0], y: point[1] };
},
strokeColor: (_point, _index, data) => {

Check warning on line 134 in client/src/components/geoJS/layers/compressedOverlayLayer.ts

View workflow job for this annotation

GitHub Actions / Lint [eslint]

'_point' is defined but never used

Check warning on line 134 in client/src/components/geoJS/layers/compressedOverlayLayer.ts

View workflow job for this annotation

GitHub Actions / Lint [eslint]

'_index' is defined but never used

Check warning on line 134 in client/src/components/geoJS/layers/compressedOverlayLayer.ts

View workflow job for this annotation

GitHub Actions / Lint [eslint]

'data' is defined but never used
return "cyan";
},
};
}
}
4 changes: 2 additions & 2 deletions client/src/components/geoJS/layers/legendLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ export default class LegendLayer {
this.lineDataX.push({ line: xAxis });
this.drawYAxis(0);

if (this.spectroInfo.start_times && this.spectroInfo.end_times) {
if (this.spectroInfo.compressedWidth && this.spectroInfo.start_times && this.spectroInfo.end_times) {
this.drawXAxisLabelsCompressed(bottomOffset, topOffset, lefOffset);
} else {
this.drawXAxisLabels(bottomOffset, topOffset, lefOffset);
Expand All @@ -405,7 +405,7 @@ export default class LegendLayer {
this.lineDataX.push({ line: xAxis });
this.drawYAxis();

if (this.spectroInfo.start_times && this.spectroInfo.end_times) {
if (this.spectroInfo.compressedWidth && this.spectroInfo.start_times && this.spectroInfo.end_times) {
this.drawXAxisLabelsCompressed();
} else {
this.drawXAxisLabels();
Expand Down
4 changes: 4 additions & 0 deletions client/src/views/Spectrogram.vue
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ export default defineComponent({
loadedImage.value = true;
};
spectroInfo.value = response.data["spectroInfo"];
if (response.data['compressed'] && spectroInfo.value) {
spectroInfo.value.start_times = response.data.compressed.start_times;
spectroInfo.value.end_times = response.data.compressed.end_times;
}
annotations.value =
response.data["annotations"]?.sort(
(a, b) => a.start_time - b.start_time
Expand Down

0 comments on commit cecc903

Please sign in to comment.