Skip to content

Commit 151d07d

Browse files
authored
Merge pull request #245 from Kitware/issue-201-nabat-bounding-box
Add a bounding box tool for NABat spectrograms
2 parents df8e29d + 5775c0e commit 151d07d

File tree

8 files changed

+367
-36
lines changed

8 files changed

+367
-36
lines changed

client/src/components/geoJS/LayerManager.vue

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
import { defineComponent, nextTick, onMounted, onUnmounted, PropType, Ref, ref, watch } from "vue";
33
import * as d3 from "d3";
44
import { SpectrogramAnnotation, SpectrogramSequenceAnnotation } from "../../api/api";
5-
import { geojsonToSpectro, SpectroInfo, textColorFromBackground } from "./geoJSUtils";
5+
import {
6+
annotationSpreadAcrossPulsesWarning,
7+
geojsonToSpectro,
8+
SpectroInfo,
9+
textColorFromBackground,
10+
} from "./geoJSUtils";
611
import EditAnnotationLayer from "./layers/editAnnotationLayer";
712
import RectangleLayer from "./layers/rectangleLayer";
813
import CompressedOverlayLayer from "./layers/compressedOverlayLayer";
@@ -13,6 +18,7 @@ import FreqLayer from "./layers/freqLayer";
1318
import SpeciesLayer from "./layers/speciesLayer";
1419
import SpeciesSequenceLayer from "./layers/speciesSequenceLayer";
1520
import MeasureToolLayer from "./layers/measureToolLayer";
21+
import BoundingBoxLayer from "./layers/boundingBoxLayer";
1622
import { cloneDeep } from "lodash";
1723
import useState from "@use/useState";
1824
export default defineComponent({
@@ -66,6 +72,8 @@ export default defineComponent({
6672
backgroundColor,
6773
measuring,
6874
frequencyRulerY,
75+
drawingBoundingBox,
76+
boundingBoxError,
6977
} = useState();
7078
const selectedAnnotationId: Ref<null | number> = ref(null);
7179
const hoveredAnnotationId: Ref<null | number> = ref(null);
@@ -83,6 +91,7 @@ export default defineComponent({
8391
let speciesLayer: SpeciesLayer;
8492
let speciesSequenceLayer: SpeciesSequenceLayer;
8593
let measureToolLayer: MeasureToolLayer;
94+
let boundingBoxLayer: BoundingBoxLayer;
8695
const displayError = ref(false);
8796
const errorMsg = ref("");
8897
@@ -214,9 +223,9 @@ export default defineComponent({
214223
if (index !== -1 && props.spectroInfo && selectedType.value === 'pulse') {
215224
// update bounds for the localAnnotation
216225
const conversionResult = geojsonToSpectro(geoJSON, props.spectroInfo, props.scaledWidth, props.scaledHeight);
217-
if (conversionResult.error) {
226+
if (conversionResult.warning) {
218227
displayError.value = true;
219-
errorMsg.value = conversionResult.error;
228+
errorMsg.value = conversionResult.warning;
220229
return;
221230
}
222231
const { low_freq, high_freq, start_time, end_time } = conversionResult;
@@ -232,9 +241,9 @@ export default defineComponent({
232241
if (index !== -1 && props.spectroInfo && selectedType.value === 'sequence') {
233242
// update bounds for the localAnnotation
234243
const conversionResult = geojsonToSpectro(geoJSON, props.spectroInfo, props.scaledWidth, props.scaledHeight);
235-
if (conversionResult.error) {
244+
if (conversionResult.warning && conversionResult.warning !== annotationSpreadAcrossPulsesWarning) {
236245
displayError.value = true;
237-
errorMsg.value = conversionResult.error;
246+
errorMsg.value = conversionResult.warning;
238247
return;
239248
}
240249
const { start_time, end_time } = conversionResult;
@@ -253,9 +262,11 @@ export default defineComponent({
253262
if (geoJSON && props.spectroInfo) {
254263
const conversionResult = geojsonToSpectro(geoJSON, props.spectroInfo, props.scaledWidth, props.scaledHeight);
255264
256-
if (conversionResult.error) {
265+
if (conversionResult.warning
266+
&& !(creationType.value === 'sequence' && conversionResult.warning === annotationSpreadAcrossPulsesWarning)
267+
) {
257268
displayError.value = true;
258-
errorMsg.value = conversionResult.error;
269+
errorMsg.value = conversionResult.warning;
259270
return;
260271
}
261272
const { low_freq, high_freq, start_time, end_time } = conversionResult;
@@ -292,6 +303,10 @@ export default defineComponent({
292303
const { yValue } = data;
293304
frequencyRulerY.value = yValue || 0;
294305
}
306+
if (type === "bbox:error") {
307+
const { error } = data;
308+
boundingBoxError.value = error || '';
309+
}
295310
};
296311
297312
const getDataForLayers = () => {
@@ -537,6 +552,18 @@ export default defineComponent({
537552
}
538553
});
539554
555+
if (!boundingBoxLayer) {
556+
boundingBoxLayer = new BoundingBoxLayer(props.geoViewerRef, event, props.spectroInfo, drawingBoundingBox.value);
557+
boundingBoxLayer.setScaledDimensions(props.scaledWidth, props.scaledHeight);
558+
}
559+
watch(drawingBoundingBox, () => {
560+
if (drawingBoundingBox.value) {
561+
boundingBoxLayer.enableDrawing();
562+
} else {
563+
boundingBoxLayer.disableDrawing();
564+
}
565+
});
566+
540567
timeLayer.setDisplaying({ pulse: configuration.value.display_pulse_annotations, sequence: configuration.value.display_sequence_annotations });
541568
timeLayer.formatData(localAnnotations.value, sequenceAnnotations.value);
542569
freqLayer.formatData(localAnnotations.value);
@@ -593,7 +620,7 @@ export default defineComponent({
593620
compressedOverlayLayer.formatData(props.spectroInfo.start_times, props.spectroInfo.end_times, props.yScale);
594621
compressedOverlayLayer.redraw();
595622
} else {
596-
compressedOverlayLayer?.disable();
623+
compressedOverlayLayer?.disable();
597624
}
598625
}
599626
editAnnotationLayer?.setScaledDimensions(props.scaledWidth, props.scaledHeight);
@@ -714,6 +741,9 @@ export default defineComponent({
714741
if (measureToolLayer) {
715742
measureToolLayer.setTextColor(textColor);
716743
}
744+
if (boundingBoxLayer) {
745+
boundingBoxLayer.setTextColor(textColor);
746+
}
717747
}
718748
719749
watch([backgroundColor, colorScheme], updateColorFilter);

client/src/components/geoJS/geoJSUtils.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { ref, Ref } from "vue";
22
import geo from "geojs";
33

4+
const annotationSpreadAcrossPulsesWarning = 'Start or End Time spread across pulses. This is not allowed in compressed annotations';
5+
46
const useGeoJS = () => {
57
// eslint-disable-next-line @typescript-eslint/no-explicit-any
68
const geoViewer: Ref<any> = ref();
@@ -506,7 +508,7 @@ function geojsonToSpectro(
506508
spectroInfo: SpectroInfo,
507509
scaledWidth = 0,
508510
scaledHeight = 0,
509-
): { error?: string; start_time: number; end_time: number; low_freq: number; high_freq: number } {
511+
): { warning?: string; start_time: number; end_time: number; low_freq: number; high_freq: number } {
510512
const adjustedWidth = scaledWidth > spectroInfo.width ? scaledWidth : spectroInfo.width;
511513
const adjustedHeight = scaledHeight > spectroInfo.height ? scaledHeight : spectroInfo.height;
512514

@@ -543,6 +545,9 @@ function geojsonToSpectro(
543545
let additivePixels = 0;
544546
let start_time = -1;
545547
let end_time = -1;
548+
let warn = false;
549+
let startIndex = 0;
550+
let endIndex = 0;
546551
for (let i = 0; i < start_times.length; i += 1) {
547552
// convert the start/end time to a pixel
548553
const nextPixels = (widths && widths[i]) || 0;
@@ -552,6 +557,7 @@ function geojsonToSpectro(
552557
const lowPixels = start - additivePixels;
553558
const lowTime = start_times[i] + lowPixels / timeToPixels;
554559
start_time = Math.round(lowTime);
560+
startIndex = i;
555561
}
556562
if (
557563
end_time === -1 &&
@@ -562,17 +568,20 @@ function geojsonToSpectro(
562568
const highPixels = end - additivePixels;
563569
const highTime = start_times[i] + highPixels / timeToPixels;
564570
end_time = Math.round(highTime);
571+
endIndex = i;
565572
}
566573
additivePixels += nextPixels;
567574
}
575+
if (startIndex !== endIndex) {
576+
warn = true;
577+
}
568578
const heightScale = adjustedHeight / (spectroInfo.high_freq - spectroInfo.low_freq);
569579
const high_freq = Math.round(spectroInfo.high_freq - coords[1][1] / heightScale);
570580
const low_freq = Math.round(spectroInfo.high_freq - coords[3][1] / heightScale);
571-
if (start_time === -1 || end_time === -1) {
581+
if (warn) {
572582
// the time spreads across multiple pulses and isn't allowed;
573583
return {
574-
error:
575-
"Start or End Time spread across pusles. This is not allowed in compressed annotations",
584+
warning: annotationSpreadAcrossPulsesWarning,
576585
start_time,
577586
end_time,
578587
low_freq,
@@ -648,12 +657,23 @@ function textColorFromBackground(rgbString: string): "black" | "white" {
648657
return getContrastingColor(r, g, b);
649658
}
650659

660+
/**
661+
* correct matching of drag handle to cursor direction relies on strict ordering of
662+
* vertices within the GeoJSON coordinate list using utils.reOrdergeoJSON()
663+
* and utils.reOrderBounds()
664+
*/
665+
const rectVertex = ["sw-resize", "nw-resize", "ne-resize", "se-resize"];
666+
const rectEdge = ["w-resize", "n-resize", "e-resize", "s-resize"];
667+
651668
export {
652669
spectroToGeoJSon,
653670
geojsonToSpectro,
654671
reOrdergeoJSON,
655672
useGeoJS,
656673
spectroToCenter,
657674
spectroSequenceToGeoJSon,
658-
textColorFromBackground
675+
textColorFromBackground,
676+
rectVertex,
677+
rectEdge,
678+
annotationSpreadAcrossPulsesWarning,
659679
};

0 commit comments

Comments
 (0)