From 603861e146906a5bdd0447cb8f168a712aee162a Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Thu, 11 Jan 2024 10:31:59 -0500 Subject: [PATCH 1/4] init edit annotations --- client/src/components/geoJS/LayerManager.vue | 28 +++++++++++++++++-- .../geoJS/layers/editAnnotationLayer.ts | 3 -- .../components/geoJS/layers/rectangleLayer.ts | 8 ++++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/client/src/components/geoJS/LayerManager.vue b/client/src/components/geoJS/LayerManager.vue index da6e69b..9a34786 100644 --- a/client/src/components/geoJS/LayerManager.vue +++ b/client/src/components/geoJS/LayerManager.vue @@ -1,5 +1,5 @@ \ No newline at end of file + diff --git a/client/src/components/geoJS/layers/editAnnotationLayer.ts b/client/src/components/geoJS/layers/editAnnotationLayer.ts index 5f86058..322c0c4 100644 --- a/client/src/components/geoJS/layers/editAnnotationLayer.ts +++ b/client/src/components/geoJS/layers/editAnnotationLayer.ts @@ -89,11 +89,11 @@ export default class EditAnnotationLayer { ) { (this.geoViewerRef = geoViewerRef), (this.event = event); this.type = "rectangle"; - this.style = { - strokeColor: 'black', - strokeWidth: 1.0, - antialiasing: 0, - }; + this.style = { + strokeColor: "black", + strokeWidth: 1.0, + antialiasing: 0, + }; this.formattedData = []; this.spectroInfo = spectroInfo; this.skipNextExternalUpdate = false; @@ -196,12 +196,10 @@ export default class EditAnnotationLayer { // triggers a mouse up while editing to make it seem like a point was placed window.setTimeout( () => - this.geoViewerRef - .interactor() - .simulateEvent("mouseup", { - map: { x: e.mouse.geo.x, y: e.mouse.geo.y }, - button: "left", - }), + this.geoViewerRef.interactor().simulateEvent("mouseup", { + map: { x: e.mouse.geo.x, y: e.mouse.geo.y }, + button: "left", + }), 0 ); } else if (this.shapeInProgress) { @@ -316,7 +314,7 @@ export default class EditAnnotationLayer { } /** overrides default function to disable and clear anotations before drawing again */ - async changeData(frameData: SpectrogramAnnotation) { + async changeData(frameData: SpectrogramAnnotation | null) { if (this.skipNextExternalUpdate === false) { // disable resets things before we load a new/different shape or mode this.disable(); @@ -341,25 +339,40 @@ export default class EditAnnotationLayer { * * @param frameData a single FrameDataTrack Array that is the editing item */ - formatData(annotationData: SpectrogramAnnotation) { + formatData(annotationData: SpectrogramAnnotation | null) { this.selectedHandleIndex = -1; this.hoverHandleIndex = -1; this.event("update:selectedIndex", { selectedIndex: this.selectedHandleIndex, selectedKey: this.selectedKey, }); - const geoJSONData = spectroToGeoJSon(annotationData, this.spectroInfo); - const geojsonFeature: GeoJSON.Feature = { - type: "Feature", - geometry: geoJSONData, - properties: { - annotationType: typeMapper.get(this.type), - }, - }; - this.featureLayer.geojson(geojsonFeature); - const annotation = this.applyStylesToAnnotations(); - this.setMode("rectangle", annotation); - this.formattedData = [geojsonFeature]; + if (annotationData) { + const geoJSONData = spectroToGeoJSon(annotationData, this.spectroInfo); + const geojsonFeature: GeoJSON.Feature = { + type: "Feature", + geometry: geoJSONData, + properties: { + annotationType: typeMapper.get(this.type), + }, + }; + this.featureLayer.geojson(geojsonFeature); + const annotation = this.applyStylesToAnnotations(); + this.setMode("rectangle", annotation); + this.formattedData = [geojsonFeature]; + return; + } else { + this.setMode(this.type); + } + if (typeof this.type !== "string") { + throw new Error( + `editing props needs to be a string of value + ${geo.listAnnotations().join(", ")} + when geojson prop is not set` + ); + } else { + // point or rectangle mode for the editor + this.setMode(this.type); + } } /** @@ -412,10 +425,12 @@ export default class EditAnnotationLayer { ); // The corners need to update for the indexes to update // coordinates are in a different system than display - const coords = (newGeojson.geometry.coordinates[0] as GeoJSON.Position[]).map((coord) => ({ - x: coord[0], - y: coord[1], - })); + const coords = (newGeojson.geometry.coordinates[0] as GeoJSON.Position[]).map( + (coord) => ({ + x: coord[0], + y: coord[1], + }) + ); // only use the 4 coords instead of 5 const remapped = this.geoViewerRef.worldToGcs(coords.splice(0, 4)); e.annotation.options("corners", remapped); @@ -443,7 +458,7 @@ export default class EditAnnotationLayer { type: this.type, selectedKey: this.selectedKey, }); - } + } } } } @@ -473,7 +488,7 @@ export default class EditAnnotationLayer { fill: false, }, fill: false, - strokeColor: "red", + strokeColor: "cyan", }; } diff --git a/client/src/components/geoJS/layers/rectangleLayer.ts b/client/src/components/geoJS/layers/rectangleLayer.ts index 41f858c..57d2a02 100644 --- a/client/src/components/geoJS/layers/rectangleLayer.ts +++ b/client/src/components/geoJS/layers/rectangleLayer.ts @@ -1,163 +1,162 @@ /* eslint-disable class-methods-use-this */ -import geo, { GeoEvent } from 'geojs'; -import { SpectroInfo, spectroToGeoJSon } from '../geoJSUtils'; -import { SpectrogramAnnotation } from '../../../api/api'; -import { LayerStyle } from './types'; +import geo, { GeoEvent } from "geojs"; +import { SpectroInfo, spectroToGeoJSon } from "../geoJSUtils"; +import { SpectrogramAnnotation } from "../../../api/api"; +import { LayerStyle } from "./types"; -interface RectGeoJSData{ +interface RectGeoJSData { id: number; selected: boolean; - editing: boolean | string; + editing?: boolean; polygon: GeoJSON.Polygon; } -export default class RectangleLayer{ - formattedData: RectGeoJSData[]; +export default class RectangleLayer { + formattedData: RectGeoJSData[]; - drawingOther: boolean; //drawing another type of annotation at the same time? + drawingOther: boolean; //drawing another type of annotation at the same time? - hoverOn: boolean; //to turn over annnotations on - // eslint-disable-next-line @typescript-eslint/no-explicit-any - featureLayer: any; + hoverOn: boolean; //to turn over annnotations on + // eslint-disable-next-line @typescript-eslint/no-explicit-any + featureLayer: any; - selectedIndex: number[]; // sparse array + selectedIndex: number[]; // sparse array - // eslint-disable-next-line @typescript-eslint/no-explicit-any - geoViewerRef: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + geoViewerRef: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - event: (name: string, data: any) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + event: (name: string, data: any) => void; - spectroInfo: SpectroInfo; + spectroInfo: SpectroInfo; - style: LayerStyle; + style: LayerStyle; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(geoViewerRef: any, event: (name: string, data: any) => void, spectroInfo: SpectroInfo) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor( + geoViewerRef: any, + event: (name: string, data: any) => void, + spectroInfo: SpectroInfo + ) { this.geoViewerRef = geoViewerRef; - this.drawingOther = false; - this.spectroInfo = spectroInfo; - this.formattedData = []; - this.hoverOn = false; - this.selectedIndex = []; - this.event = event; - //Only initialize once, prevents recreating Layer each edit - const layer = this.geoViewerRef.createLayer('feature', { - features: ['polygon'], - }); - this.featureLayer = layer - .createFeature('polygon', { selectionAPI: true }) - .geoOn(geo.event.feature.mouseclick, (e: GeoEvent) => { + this.drawingOther = false; + this.spectroInfo = spectroInfo; + this.formattedData = []; + this.hoverOn = false; + this.selectedIndex = []; + this.event = event; + //Only initialize once, prevents recreating Layer each edit + const layer = this.geoViewerRef.createLayer("feature", { + features: ["polygon"], + }); + this.featureLayer = layer + .createFeature("polygon", { selectionAPI: true }) + .geoOn(geo.event.feature.mouseclick, (e: GeoEvent) => { /** * Handle clicking on individual annotations, if DrawingOther is true we use the * Rectangle type if only the polygon is visible we use the polygon bounds * */ - if (e.mouse.buttonsDown.left) { - if (!e.data.editing || (e.data.editing && !e.data.selected)) { - this.event('annotation-clicked', { id: e.data.id, edit: false }); - } - } else if (e.mouse.buttonsDown.right) { - if (!e.data.editing || (e.data.editing && !e.data.selected)) { - this.event('annotation-right-clicked', { id: e.data.id, edit: true }); - } + if (e.mouse.buttonsDown.left) { + if (!e.data.editing || (e.data.editing && !e.data.selected)) { + this.event("annotation-clicked", { id: e.data.id, edit: false }); + } + } else if (e.mouse.buttonsDown.right) { + if (!e.data.editing || (e.data.editing && !e.data.selected)) { + this.event("annotation-right-clicked", { id: e.data.id, edit: true }); } - }); - this.featureLayer.geoOn( - geo.event.feature.mouseclick_order, - this.featureLayer.mouseOverOrderClosestBorder, - ); - this.featureLayer.geoOn(geo.event.mouseclick, (e: GeoEvent) => { - // If we aren't clicking on an annotation we can deselect the current track - if (this.featureLayer.pointSearch(e.geo).found.length === 0) { - this.event('annotation-clicked', { id: null, edit: false }); } }); - this.style = this.createStyle(); - } - - hoverAnnotations(e: GeoEvent) { - const { found } = this.featureLayer.pointSearch(e.mouse.geo); - this.event('annotation-hover', {id: found, pos: e.mouse.geo}); - } - - setHoverAnnotations(val: boolean) { - if (!this.hoverOn && val) { - this.featureLayer.geoOn( - geo.event.feature.mouseover, - (e: GeoEvent) => this.hoverAnnotations(e), - ); - this.featureLayer.geoOn( - geo.event.feature.mouseoff, - (e: GeoEvent) => this.hoverAnnotations(e), - ); - this.hoverOn = true; - } else if (this.hoverOn && !val) { - this.featureLayer.geoOff(geo.event.feature.mouseover); - this.featureLayer.geoOff(geo.event.feature.mouseoff); - this.hoverOn = false; + this.featureLayer.geoOn( + geo.event.feature.mouseclick_order, + this.featureLayer.mouseOverOrderClosestBorder + ); + this.featureLayer.geoOn(geo.event.mouseclick, (e: GeoEvent) => { + // If we aren't clicking on an annotation we can deselect the current track + if (this.featureLayer.pointSearch(e.geo).found.length === 0) { + this.event("annotation-cleared", { id: null, edit: false }); } + }); + this.style = this.createStyle(); + } + + hoverAnnotations(e: GeoEvent) { + const { found } = this.featureLayer.pointSearch(e.mouse.geo); + this.event("annotation-hover", { id: found, pos: e.mouse.geo }); + } + + setHoverAnnotations(val: boolean) { + if (!this.hoverOn && val) { + this.featureLayer.geoOn(geo.event.feature.mouseover, (e: GeoEvent) => + this.hoverAnnotations(e) + ); + this.featureLayer.geoOn(geo.event.feature.mouseoff, (e: GeoEvent) => + this.hoverAnnotations(e) + ); + this.hoverOn = true; + } else if (this.hoverOn && !val) { + this.featureLayer.geoOff(geo.event.feature.mouseover); + this.featureLayer.geoOff(geo.event.feature.mouseoff); + this.hoverOn = false; } + } - /** + /** * Used to set the drawingOther parameter used to change styling if other types are drawn * and also handle selection clicking between different types * @param val - determines if we are drawing other types of annotations */ - setDrawingOther(val: boolean) { - this.drawingOther = val; - } - + setDrawingOther(val: boolean) { + this.drawingOther = val; + } - formatData(annotationData: SpectrogramAnnotation[], selectedIndex: number | null) { - const arr: RectGeoJSData[] = []; - annotationData.forEach((annotation: SpectrogramAnnotation, ) => { + formatData(annotationData: SpectrogramAnnotation[], selectedIndex: number | null) { + const arr: RectGeoJSData[] = []; + annotationData.forEach((annotation: SpectrogramAnnotation) => { const polygon = spectroToGeoJSon(annotation, this.spectroInfo); const newAnnotation: RectGeoJSData = { - id: annotation.id, - selected: annotation.id === selectedIndex, - editing: false, - polygon, - }; + id: annotation.id, + selected: annotation.id === selectedIndex, + editing: annotation.editing, + polygon, + }; arr.push(newAnnotation); - - }); - this.formattedData = arr; - } - - redraw() { - // add some styles - this.featureLayer - .data(this.formattedData) - .polygon((d: RectGeoJSData) => d.polygon.coordinates[0]) - .style(this.createStyle()) - .draw(); - } - - disable() { - this.featureLayer - .data([]) - .draw(); - } - - createStyle(): LayerStyle { - return { - ...{ - strokeColor: 'black', - strokeWidth: 4.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) => { - if (data.selected) { - return 'cyan'; - } - return 'red'; - }, }; + }); + this.formattedData = arr; + } + + redraw() { + // add some styles + this.featureLayer + .data(this.formattedData) + .polygon((d: RectGeoJSData) => d.polygon.coordinates[0]) + .style(this.createStyle()) + .draw(); + } + + disable() { + this.featureLayer.data([]).draw(); + } + + createStyle(): LayerStyle { + return { + ...{ + strokeColor: "black", + strokeWidth: 4.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) => { + if (data.selected) { + return "cyan"; } + return "red"; + }, + }; + } } From bd3a05a953653d33a739844cc59b0459480fd86b Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Mon, 15 Jan 2024 12:56:35 -0500 Subject: [PATCH 3/4] editing annotations --- client/package-lock.json | 7 ++ client/package.json | 1 + client/src/api/api.ts | 9 +- client/src/components/AnnotationEditor.vue | 70 ++++++++++++++ client/src/components/AnnotationList.vue | 99 ++++++++++++++++++++ client/src/components/SpectrogramViewer.vue | 10 +- client/src/components/geoJS/LayerManager.vue | 40 +++++++- client/src/components/geoJS/geoJSUtils.ts | 4 +- client/src/views/Spectrogram.vue | 59 ++++++++++-- client/yarn.lock | 5 + 10 files changed, 287 insertions(+), 17 deletions(-) create mode 100644 client/src/components/AnnotationEditor.vue create mode 100644 client/src/components/AnnotationList.vue diff --git a/client/package-lock.json b/client/package-lock.json index a0b5feb..d05e0be 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -24,6 +24,7 @@ "devDependencies": { "@types/geojson": "^7946.0.13", "@types/jest": "^27.4.1", + "@types/lodash": "^4.14.202", "@vitejs/plugin-vue": "^2.2.0", "@vue/eslint-config-typescript": "^10.0.0", "@vuetify/vite-plugin": "^1.0.0-alpha.10", @@ -3066,6 +3067,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "devOptional": true }, + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, "node_modules/@types/node": { "version": "17.0.21", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", diff --git a/client/package.json b/client/package.json index 2cd9945..652ce37 100644 --- a/client/package.json +++ b/client/package.json @@ -29,6 +29,7 @@ "devDependencies": { "@types/geojson": "^7946.0.13", "@types/jest": "^27.4.1", + "@types/lodash": "^4.14.202", "@vitejs/plugin-vue": "^2.2.0", "@vue/eslint-config-typescript": "^10.0.0", "@vuetify/vite-plugin": "^1.0.0-alpha.10", diff --git a/client/src/api/api.ts b/client/src/api/api.ts index 7a07d99..76a86fa 100644 --- a/client/src/api/api.ts +++ b/client/src/api/api.ts @@ -50,7 +50,9 @@ export interface SpectrogramAnnotation { low_freq: number; high_freq: number; id: number; - editing?: boolean + editing?: boolean; + species?: Species[]; + comments?: string; } @@ -107,9 +109,12 @@ async function getSpectrogram(id: string) { return axiosInstance.get(`/recording/${id}/spectrogram`); } - +async function getSpecies() { + return axiosInstance.get('/species/'); +} export { uploadRecordingFile, getRecordings, getSpectrogram, + getSpecies, }; \ No newline at end of file diff --git a/client/src/components/AnnotationEditor.vue b/client/src/components/AnnotationEditor.vue new file mode 100644 index 0000000..bedb9fe --- /dev/null +++ b/client/src/components/AnnotationEditor.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/client/src/components/AnnotationList.vue b/client/src/components/AnnotationList.vue new file mode 100644 index 0000000..5fe23b5 --- /dev/null +++ b/client/src/components/AnnotationList.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/client/src/components/SpectrogramViewer.vue b/client/src/components/SpectrogramViewer.vue index e07a896..f45a416 100644 --- a/client/src/components/SpectrogramViewer.vue +++ b/client/src/components/SpectrogramViewer.vue @@ -21,7 +21,12 @@ export default defineComponent({ annotations: { type: Array as PropType, default: () => [], + }, + selectedId: { + type: Number as PropType, + default: null, } + }, setup(props) { const containerRef: Ref = ref(); @@ -57,6 +62,8 @@ export default defineComponent({ :geo-viewer-ref="geoViewerRef" :spectro-info="spectroInfo" :annotations="annotations" + :selected-id="selectedId" + @selected="$emit('selected',$event)" /> @@ -69,8 +76,7 @@ export default defineComponent({ top: 0; bottom: 0; z-index: 0; - width:100vw; - height: 100vh; + height: 90vh; background-color: black; display: flex; diff --git a/client/src/components/geoJS/LayerManager.vue b/client/src/components/geoJS/LayerManager.vue index 91c45b5..9ade016 100644 --- a/client/src/components/geoJS/LayerManager.vue +++ b/client/src/components/geoJS/LayerManager.vue @@ -1,7 +1,7 @@ diff --git a/client/yarn.lock b/client/yarn.lock index 093b34c..8700ec4 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1717,6 +1717,11 @@ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== +"@types/lodash@^4.14.202": + version "4.14.202" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz" + integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ== + "@types/node@*": version "17.0.21" resolved "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz" From b4dcc3065ae016c4f4e8da87bdc69ac63296cd26 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Wed, 17 Jan 2024 10:43:43 -0500 Subject: [PATCH 4/4] basic of adding new annotations --- bats_ai/core/views/recording.py | 27 ++++- client/src/api/api.ts | 33 ++++++ client/src/components/AnnotationEditor.vue | 56 ++++++++- client/src/components/AnnotationList.vue | 26 ++++- client/src/components/SpectrogramViewer.vue | 31 ++++- client/src/components/UploadRecording.vue | 3 +- client/src/components/geoJS/LayerManager.vue | 108 +++++++++++++----- client/src/components/geoJS/geoJSUtils.ts | 8 +- .../geoJS/layers/editAnnotationLayer.ts | 1 + .../components/geoJS/layers/rectangleLayer.ts | 2 + client/src/use/useState.ts | 15 +++ client/src/views/Spectrogram.vue | 16 ++- 12 files changed, 271 insertions(+), 55 deletions(-) create mode 100644 client/src/use/useState.ts diff --git a/bats_ai/core/views/recording.py b/bats_ai/core/views/recording.py index faff64a..f77aae0 100644 --- a/bats_ai/core/views/recording.py +++ b/bats_ai/core/views/recording.py @@ -48,6 +48,16 @@ class AnnotationSchema(Schema): id: int +class UpdateAnnotationsSchema(Schema): + start_time: int | None + end_time: int | None + low_freq: int | None + high_freq: int | None + species: list[SpeciesSchema] | None + comments: str | None + id: int | None + + def get_user(request: HttpRequest): auth_header = request.headers.get('Authorization', None) if auth_header is not None: @@ -179,7 +189,7 @@ def patch_annotation( request, recording_id: int, id: int, - annotation: AnnotationSchema, + annotation: UpdateAnnotationsSchema, species_ids: list[int], ): user_id = get_user(request) @@ -193,11 +203,16 @@ def patch_annotation( return {'error': 'Annotation not found'} # Update annotation details - annotation_instance.start_time = annotation.start_time - annotation_instance.end_time = annotation.end_time - annotation_instance.low_freq = annotation.low_freq - annotation_instance.high_freq = annotation.high_freq - annotation_instance.comments = annotation.comments + if annotation.start_time: + annotation_instance.start_time = annotation.start_time + if annotation.end_time: + annotation_instance.end_time = annotation.end_time + if annotation.low_freq: + annotation_instance.low_freq = annotation.low_freq + if annotation.high_freq: + annotation_instance.high_freq = annotation.high_freq + if annotation.comments: + annotation_instance.comments = annotation.comments annotation_instance.save() # Clear existing species associations diff --git a/client/src/api/api.ts b/client/src/api/api.ts index 76a86fa..19662e3 100644 --- a/client/src/api/api.ts +++ b/client/src/api/api.ts @@ -42,6 +42,7 @@ export interface Species { genus: string; common_name: string; species_code_6?: string; + id: number; } export interface SpectrogramAnnotation { @@ -55,6 +56,16 @@ export interface SpectrogramAnnotation { comments?: string; } +export interface UpdateSpectrogramAnnotation { + start_time?: number; + end_time?: number; + low_freq?: number; + high_freq?: number; + id?: number; + editing?: boolean; + species?: Species[]; + comments?: string; +} export interface Spectrogram { 'base64_spectrogram': string; @@ -109,12 +120,34 @@ async function getSpectrogram(id: string) { return axiosInstance.get(`/recording/${id}/spectrogram`); } +async function getAnnotations(recordingId: string) { + return axiosInstance.get(`/recording/${recordingId}/annotations`); + +} + async function getSpecies() { return axiosInstance.get('/species/'); } + +async function patchAnnotation(recordingId: string, annotationId: number, annotation: UpdateSpectrogramAnnotation, speciesList: number[] = []) { + return axiosInstance.patch(`/recording/${recordingId}/annotations/${annotationId}`, { annotation, species_ids: speciesList}); +} + +async function putAnnotation(recordingId: string, annotation: UpdateSpectrogramAnnotation, speciesList: number[] = []) { + return axiosInstance.put<{message: string, id: number}>(`/recording/${recordingId}/annotations`, { annotation, species_ids: speciesList}); +} + +async function deleteAnnotation(recordingId: string, annotationId: number) { + return axiosInstance.delete(`/recording/${recordingId}/annotations/${annotationId}`); +} + export { uploadRecordingFile, getRecordings, getSpectrogram, getSpecies, + getAnnotations, + patchAnnotation, + putAnnotation, + deleteAnnotation }; \ No newline at end of file diff --git a/client/src/components/AnnotationEditor.vue b/client/src/components/AnnotationEditor.vue index bedb9fe..1245061 100644 --- a/client/src/components/AnnotationEditor.vue +++ b/client/src/components/AnnotationEditor.vue @@ -1,7 +1,7 @@ diff --git a/client/src/components/geoJS/geoJSUtils.ts b/client/src/components/geoJS/geoJSUtils.ts index f0048a5..048fa70 100644 --- a/client/src/components/geoJS/geoJSUtils.ts +++ b/client/src/components/geoJS/geoJSUtils.ts @@ -188,10 +188,10 @@ function geojsonToSpectro(geojson: GeoJSON.Feature, spectroInfo const coords = geojson.geometry.coordinates[0]; const widthScale = spectroInfo.width / (spectroInfo.end_time - spectroInfo.start_time); const heightScale = spectroInfo.height / (spectroInfo.high_freq - spectroInfo.low_freq); - const start_time = coords[1][0] / widthScale; - const end_time = coords[3][0] / widthScale; - const low_freq = spectroInfo.high_freq - (coords[1][1]) / heightScale; - const high_freq = spectroInfo.high_freq - (coords[3][1]) / heightScale; + const start_time = Math.round(coords[1][0] / widthScale); + const end_time = Math.round(coords[3][0] / widthScale); + const low_freq = Math.round(spectroInfo.high_freq - (coords[1][1]) / heightScale); + const high_freq = Math.round(spectroInfo.high_freq - (coords[3][1]) / heightScale); return { start_time, end_time, diff --git a/client/src/components/geoJS/layers/editAnnotationLayer.ts b/client/src/components/geoJS/layers/editAnnotationLayer.ts index 322c0c4..da6216f 100644 --- a/client/src/components/geoJS/layers/editAnnotationLayer.ts +++ b/client/src/components/geoJS/layers/editAnnotationLayer.ts @@ -398,6 +398,7 @@ export default class EditAnnotationLayer { this.event("update:geojson", { status: "editing", creating: this.getMode() === "creation", + geoJSON: geoJSONData[0], type: this.type, selectedKey: this.selectedKey, }); diff --git a/client/src/components/geoJS/layers/rectangleLayer.ts b/client/src/components/geoJS/layers/rectangleLayer.ts index 57d2a02..e98a16e 100644 --- a/client/src/components/geoJS/layers/rectangleLayer.ts +++ b/client/src/components/geoJS/layers/rectangleLayer.ts @@ -34,7 +34,9 @@ export default class RectangleLayer { // 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 event: (name: string, data: any) => void, spectroInfo: SpectroInfo ) { diff --git a/client/src/use/useState.ts b/client/src/use/useState.ts new file mode 100644 index 0000000..ac9ca6d --- /dev/null +++ b/client/src/use/useState.ts @@ -0,0 +1,15 @@ +import { ref, Ref } from 'vue'; + +const annotationState: Ref = ref(''); + +type AnnotationState = '' | 'editing' | 'creating'; +export default function useState() { + const setAnnotationState = (state: AnnotationState) => { + annotationState.value = state; + }; + return { + annotationState, + setAnnotationState, + }; +} + diff --git a/client/src/views/Spectrogram.vue b/client/src/views/Spectrogram.vue index cade989..1b3b0b9 100644 --- a/client/src/views/Spectrogram.vue +++ b/client/src/views/Spectrogram.vue @@ -1,6 +1,6 @@