From 9394f3df8ce9f7a474abe252db8dfc8b11fbc486 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Thu, 29 Feb 2024 09:56:21 -0500 Subject: [PATCH] upload metadata, scaling annotations, duration labels --- client/src/components/AnnotationEditor.vue | 20 ++++-- client/src/components/AnnotationList.vue | 2 + client/src/components/HelpSystem.vue | 16 ++++- client/src/components/TemporalList.vue | 1 + client/src/components/UploadRecording.vue | 29 +++++--- client/src/components/geoJS/LayerManager.vue | 9 ++- .../src/components/geoJS/layers/timeLayer.ts | 66 ++++++++++++++++++- client/src/use/useState.ts | 10 ++- client/src/views/Recordings.vue | 18 ++++- client/src/views/Spectrogram.vue | 20 +++++- 10 files changed, 166 insertions(+), 25 deletions(-) diff --git a/client/src/components/AnnotationEditor.vue b/client/src/components/AnnotationEditor.vue index 76715c1..f5c98a1 100644 --- a/client/src/components/AnnotationEditor.vue +++ b/client/src/components/AnnotationEditor.vue @@ -34,9 +34,11 @@ export default defineComponent({ }); const speciesEdit: Ref = ref( props.annotation?.species?.map((item) => item.species_code || item.common_name) || []); const comments: Ref = ref(props.annotation?.comments || ''); - const type: Ref = ref(''); + const type: Ref = ref([]); + const callTypes = ref(['Search', 'Approach', 'Terminal', 'Social']); + if (selectedType.value === 'sequence') { - type.value = (props.annotation as SpectrogramTemporalAnnotation).type || ''; + type.value = (props.annotation as SpectrogramTemporalAnnotation).type?.split('+') || ['']; } watch(() => props.annotation, () => { if (props.annotation?.species) { @@ -46,7 +48,7 @@ export default defineComponent({ comments.value = props.annotation.comments; } if (selectedType.value === 'pulse' && (props.annotation as SpectrogramTemporalAnnotation).type) { - type.value = (props.annotation as SpectrogramTemporalAnnotation).type || ''; + type.value = (props.annotation as SpectrogramTemporalAnnotation).type?.split('+') || []; } }); const updateAnnotation = async () => { @@ -62,7 +64,8 @@ export default defineComponent({ if (selectedType.value === 'pulse') { await patchAnnotation(props.recordingId, props.annotation?.id, { ...props.annotation, comments: comments.value }, speciesIds ); } else if (selectedType.value === 'sequence') { - await patchTemporalAnnotation(props.recordingId, props.annotation.id, {...props.annotation, comments: comments.value, type: type.value,}, speciesIds); + const updateType = type.value.join('+'); + await patchTemporalAnnotation(props.recordingId, props.annotation.id, {...props.annotation, comments: comments.value, type: updateType,}, speciesIds); } // Signal to redownload the updated annotation values if possible emit('update:annotation'); @@ -82,6 +85,7 @@ export default defineComponent({ }; return { + callTypes, speciesList, speciesEdit, comments, @@ -122,10 +126,14 @@ export default defineComponent({ /> - diff --git a/client/src/components/AnnotationList.vue b/client/src/components/AnnotationList.vue index 4760198..6832295 100644 --- a/client/src/components/AnnotationList.vue +++ b/client/src/components/AnnotationList.vue @@ -129,6 +129,7 @@ export default defineComponent({ {{ annotation.start_time }}-{{ annotation.end_time }}ms + ({{ annotation.end_time - annotation.start_time }}ms) {{ (annotation.low_freq/1000).toFixed(1) }}-{{ (annotation.high_freq/1000).toFixed() }}Khz @@ -184,6 +185,7 @@ export default defineComponent({ {{ annotation.start_time }}-{{ annotation.end_time }}ms + ({{ annotation.end_time - annotation.start_time }}ms) Type:{{ annotation.type }} diff --git a/client/src/components/HelpSystem.vue b/client/src/components/HelpSystem.vue index 80f881a..c19be3c 100644 --- a/client/src/components/HelpSystem.vue +++ b/client/src/components/HelpSystem.vue @@ -130,10 +130,24 @@ export default defineComponent({ color="" variant="flat" > + mdi-arrow-left-right

ms

- Toggle millisecond/timing labels + Toggle millisecond/timing endpoint labels +
+ + + + mdi-arrow-expand-horizontal +

ms

+
+
+ Toggle millisecond/timing duration labels
diff --git a/client/src/components/TemporalList.vue b/client/src/components/TemporalList.vue index cbe32eb..1346879 100644 --- a/client/src/components/TemporalList.vue +++ b/client/src/components/TemporalList.vue @@ -78,6 +78,7 @@ export default defineComponent({ {{ annotation.start_time }}-{{ annotation.end_time }}ms + ({{ annotation.end_time - annotation.start_time }}ms) = ref(); const publicVal = ref(props.editing ? props.editing.public : false); const autoFill = async (filename: string) => { - const parts = filename.split("_"); + + const regexPattern = /^(\d+)_(.+)_(\d{8})_(\d{6})(?:_(.*))?$/; + + // Match the file name against the regular expression + const match = filename.match(regexPattern); + + // If there's no match, return null + if (!match) { + return null; + } + + // Extract the matched groups + const cellId = match[1]; + const labelName = match[2]; + const date = match[3]; + const timestamp = match[4]; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const extraData = match[5] || null; // Additional data after the required parts // Extracting individual components - const cellId = parts[0]; - const quadrant = parts[1]; - const date = parts[2]; - const timestamp = parts[3]; if (cellId) { gridCellId.value = parseInt(cellId, 10); let updatedQuadrant; - if (['SW', 'NE', 'NW', 'SE'].includes(quadrant)) { - updatedQuadrant = quadrant as 'SW' | 'NE' | 'NW' | 'SE' | undefined; + if (['SW', 'NE', 'NW', 'SE'].includes(labelName)) { + updatedQuadrant = labelName as 'SW' | 'NE' | 'NW' | 'SE' | undefined; } const { latitude: lat , longitude: lon } = (await getCellLocation(gridCellId.value, updatedQuadrant)).data; if (lat && lon) { diff --git a/client/src/components/geoJS/LayerManager.vue b/client/src/components/geoJS/LayerManager.vue index d1fde84..b9b5f79 100644 --- a/client/src/components/geoJS/LayerManager.vue +++ b/client/src/components/geoJS/LayerManager.vue @@ -244,7 +244,7 @@ export default defineComponent({ } } else if (creating) { if (geoJSON && props.spectroInfo) { - const conversionResult = geojsonToSpectro(geoJSON, props.spectroInfo); + const conversionResult = geojsonToSpectro(geoJSON, props.spectroInfo, props.scaledWidth, props.scaledHeight); if (conversionResult.error) { displayError.value = true; @@ -332,7 +332,12 @@ export default defineComponent({ legendLayer.setGridEnabled(false); } legendLayer.redraw(); - if (layerVisibility.value.includes("time")) { + if (layerVisibility.value.includes("time") || layerVisibility.value.includes('duration')) { + if (layerVisibility.value.includes("time")) { + timeLayer.displayDuration = false; + } else { + timeLayer.displayDuration = true; + } timeLayer.formatData(annotations, temporalAnnotations); timeLayer.redraw(); } else { diff --git a/client/src/components/geoJS/layers/timeLayer.ts b/client/src/components/geoJS/layers/timeLayer.ts index 128d0ee..2cd2e9f 100644 --- a/client/src/components/geoJS/layers/timeLayer.ts +++ b/client/src/components/geoJS/layers/timeLayer.ts @@ -42,6 +42,7 @@ export default class TimeLayer { scaledWidth: number; scaledHeight: number; + displayDuration: boolean; // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor( @@ -68,7 +69,7 @@ export default class TimeLayer { .position((data: TextData) => ({ x: data.x, y: data.y })); this.lineLayer = layer.createFeature("line"); - + this.displayDuration = true; this.textStyle = this.createTextStyle(); this.lineStyle = this.createLineStyle(); } @@ -87,8 +88,7 @@ export default class TimeLayer { } } - - formatData(annotationData: SpectrogramAnnotation[], temporalData: SpectrogramTemporalAnnotation[] =[]) { + createRange(annotationData: SpectrogramAnnotation[], temporalData: SpectrogramTemporalAnnotation[] =[]) { this.textData = []; this.lineData = []; const lineDist = 12; @@ -193,6 +193,66 @@ export default class TimeLayer { } + createDuration(annotationData: SpectrogramAnnotation[], temporalData: SpectrogramTemporalAnnotation[] =[]) { + this.textData = []; + this.lineData = []; + const lineDist = 12; + annotationData.forEach((annotation: SpectrogramAnnotation) => { + const polygon = spectroToGeoJSon(annotation, this.spectroInfo, 1, this.scaledWidth, this.scaledHeight); + const {start_time, end_time } = annotation; + const [xmin, ymin] = polygon.coordinates[0][0]; + const [xmax, ymax] = polygon.coordinates[0][2]; + // For the compressed view we need to filter out default or NaN numbers + if (Number.isNaN(xmax) || Number.isNaN(xmin) || Number.isNaN(ymax) || Number.isNaN(ymin)) { + return; + } + if (xmax === -1 && ymin === -1 && ymax === -1 && xmin === -1) { + return; + } + const xpos = (xmin + xmax) / 2.0; + const ypos = (ymax + ymin) /2.0; + // Now we need to create the text Labels + this.textData.push({ + text: `${end_time - start_time}ms`, + x: xpos, + y: ypos + lineDist, + offsetX: 0, + offsetY: 0, + }); + }); + temporalData.forEach((annotation: SpectrogramTemporalAnnotation) => { + const polygon = spectroTemporalToGeoJSon(annotation, this.spectroInfo, -10, -50, 1, this.scaledWidth, this.scaledHeight); + const {start_time, end_time } = annotation; + const [xmin, ymin] = polygon.coordinates[0][0]; + const [xmax, ymax] = polygon.coordinates[0][2]; + // For the compressed view we need to filter out default or NaN numbers + if (Number.isNaN(xmax) || Number.isNaN(xmin) || Number.isNaN(ymax) || Number.isNaN(ymin)) { + return; + } + if (xmax === -1 && ymin === -1 && ymax === -1 && xmin === -1) { + return; + } + const xpos = (xmin + xmax) / 2.0; + // Now we need to create the text Labels + this.textData.push({ + text: `${end_time-start_time}ms`, + x: xpos, + y: (ymax - ymin) / 2.0, + offsetX: 0, + offsetY: -5, + }); + }); + + } + + formatData(annotationData: SpectrogramAnnotation[], temporalData: SpectrogramTemporalAnnotation[] =[]) { + if (!this.displayDuration) { + this.createRange(annotationData, temporalData); + } else { + this.createDuration(annotationData, temporalData); + } + } + redraw() { // add some styles this.lineLayer diff --git a/client/src/use/useState.ts b/client/src/use/useState.ts index 7fc05cd..3993926 100644 --- a/client/src/use/useState.ts +++ b/client/src/use/useState.ts @@ -5,7 +5,7 @@ import { OtherUserAnnotations, Recording, SpectrogramAnnotation, SpectrogramTemp const annotationState: Ref = ref(""); const creationType: Ref<'pulse' | 'sequence'> = ref("pulse"); -type LayersVis = "time" | "freq" | "species" | "grid" | 'temporal'; +type LayersVis = "time" | "freq" | "species" | "grid" | 'temporal' | 'duration'; const layerVisibility: Ref = ref(['temporal', 'species', 'time', 'freq']); const colorScale: Ref | undefined> = ref(); const selectedUsers: Ref = ref([]); @@ -36,6 +36,14 @@ export default function useState() { // If the value is present, remove it clone.splice(index, 1); } + if (value === 'time' && clone.includes('duration')) { + const durationInd = layerVisibility.value.indexOf('duration'); + clone.splice(durationInd, 1); + } + if (value === 'duration' && clone.includes('time')) { + const timeInd = layerVisibility.value.indexOf('time'); + clone.splice(timeInd, 1); + } layerVisibility.value = clone; } const setSelectedUsers = (newUsers: string[]) => { diff --git a/client/src/views/Recordings.vue b/client/src/views/Recordings.vue index b6749fe..b93674e 100644 --- a/client/src/views/Recordings.vue +++ b/client/src/views/Recordings.vue @@ -203,7 +203,7 @@ export default defineComponent({ :headers="headers" :items="recordingList" density="compact" - class="elevation-1" + class="elevation-1 my-recordings" > + - Turn Time Label On/Off + Turn Time Endpoint Labels On/Off + + + + Turn Time Duration Labels On/Off