Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions client/src/components/geoJS/LayerManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import TimeLayer from "./layers/timeLayer";
import FreqLayer from "./layers/freqLayer";
import SpeciesLayer from "./layers/speciesLayer";
import SpeciesSequenceLayer from "./layers/speciesSequenceLayer";
import MeasureToolLayer from "./layers/measureToolLayer";
import { cloneDeep } from "lodash";
import useState from "@use/useState";
export default defineComponent({
Expand Down Expand Up @@ -63,6 +64,8 @@ export default defineComponent({
configuration,
colorScheme,
backgroundColor,
measuring,
frequencyRulerY,
} = useState();
const selectedAnnotationId: Ref<null | number> = ref(null);
const hoveredAnnotationId: Ref<null | number> = ref(null);
Expand All @@ -79,6 +82,7 @@ export default defineComponent({
let freqLayer: FreqLayer;
let speciesLayer: SpeciesLayer;
let speciesSequenceLayer: SpeciesSequenceLayer;
let measureToolLayer: MeasureToolLayer;
const displayError = ref(false);
const errorMsg = ref("");

Expand Down Expand Up @@ -284,6 +288,10 @@ export default defineComponent({
}
}
}
if (type === "measure:dragged") {
const { yValue } = data;
frequencyRulerY.value = yValue || 0;
}
};

const getDataForLayers = () => {
Expand Down Expand Up @@ -510,6 +518,25 @@ export default defineComponent({
speciesLayer.spectroInfo = props.spectroInfo;
speciesLayer.setScaledDimensions(props.scaledWidth, props.scaledHeight);

if (!measureToolLayer) {
measureToolLayer = new MeasureToolLayer(
props.geoViewerRef,
event,
props.spectroInfo,
measuring.value,
frequencyRulerY.value
);
measureToolLayer.setScaledDimensions(props.scaledWidth, props.scaledHeight);
}
measureToolLayer.redraw();
watch(measuring, () => {
if (measuring.value) {
measureToolLayer.enableDrawing();
} else {
measureToolLayer.disableDrawing();
}
});

timeLayer.setDisplaying({ pulse: configuration.value.display_pulse_annotations, sequence: configuration.value.display_sequence_annotations });
timeLayer.formatData(localAnnotations.value, sequenceAnnotations.value);
freqLayer.formatData(localAnnotations.value);
Expand Down Expand Up @@ -608,6 +635,10 @@ export default defineComponent({
);
sequenceAnnotationLayer.redraw();
}
if (measureToolLayer) {
measureToolLayer.setScaledDimensions(props.scaledWidth, props.scaledHeight);
measureToolLayer.redraw();
}
// Triggers the Axis redraw when zoomed in and the axis is at the bottom/top
legendLayer?.onPan();
});
Expand Down Expand Up @@ -642,7 +673,7 @@ export default defineComponent({
// convert rgb(0 0 0) to rgb(0, 0, 0)
backgroundColor.value = backgroundColor.value.replace(/rgb\((\d+)\s+(\d+)\s+(\d+)\)/, 'rgb($1, $2, $3)');
}

const backgroundRgbColor = d3.color(backgroundColor.value) as d3.RGBColor;
const redStops: number[] = [backgroundRgbColor.r / 255];
const greenStops: number[] = [backgroundRgbColor.g / 255];
Expand All @@ -669,11 +700,13 @@ export default defineComponent({
}
if (timeLayer) {
timeLayer.setTextColor(textColor);
}
}
if (speciesSequenceLayer) {
speciesSequenceLayer.setTextColor(textColor);
}

if (measureToolLayer) {
measureToolLayer.setTextColor(textColor);
}
}

watch([backgroundColor, colorScheme], updateColorFilter);
Expand Down
16 changes: 1 addition & 15 deletions client/src/components/geoJS/layers/freqLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,7 @@
import { SpectrogramAnnotation } from "../../../api/api";
import { SpectroInfo, spectroToGeoJSon } from "../geoJSUtils";
import BaseTextLayer from "./baseTextLayer";
import { LayerStyle } from "./types";

interface LineData {
line: GeoJSON.LineString;
thicker?: boolean;
grid?: boolean;
}

interface TextData {
text: string;
x: number;
y: number;
offsetY?: number;
offsetX?: number;
}
import { LayerStyle, LineData, TextData } from "./types";

export default class FreqLayer extends BaseTextLayer<TextData> {
lineData: LineData[];
Expand Down
259 changes: 259 additions & 0 deletions client/src/components/geoJS/layers/measureToolLayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import geo, { GeoEvent } from 'geojs';
import { SpectroInfo } from '../geoJSUtils';
import { LayerStyle, LineData, TextData } from './types';
import BaseTextLayer from './baseTextLayer';

function _determineRulerColor(isDragging: boolean, isDarkMode: boolean) {
if (isDarkMode) {
return isDragging ? 'orange' : 'yellow';
}
return isDragging ? 'cyan' : 'blue';
}

export default class MeasureToolLayer extends BaseTextLayer<TextData> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
frequencyRulerLayer: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
pointAnnotation: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
lineAnnotation: any;

rulerOn: boolean;
dragging: boolean;
yValue: number;
hovering: boolean;

moveHandler: (e: GeoEvent) => void;
mousedownHandler: (e: GeoEvent) => void;
hoverHandler: (e: GeoEvent) => void;
mouseupHandler: () => void;

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,
measuring?: boolean,
yValue?: number
) {
super(geoViewerRef, event, spectroInfo);

const textLayer = this.geoViewerRef.createLayer('feature', {
features: ['text']
});
this.textLayer = textLayer
.createFeature("text")
.text((data: TextData) => data.text)
.style(this.createTextStyle())
.position((data: TextData) => ({
x: data.x,
y: data.y,
}));

const frequencyRulerLayer = this.geoViewerRef.createLayer("feature", {
features: ['point', 'line'],
});
this.frequencyRulerLayer = frequencyRulerLayer;
this.rulerOn = false;
this.pointAnnotation= null;
this.lineAnnotation = null;
this.dragging = false;
this.yValue = yValue || 0;
this.color = 'white';
this.hovering = false;

this.textStyle = this.createTextStyle();
this.rulerOn = measuring || false;
if (this.rulerOn) {
this.enableDrawing();
}

this.moveHandler = (e: GeoEvent) => {
if (e && this.dragging) {
this.updateRuler(e.mouse.geo.y);
}
};
this.hoverHandler = (e: GeoEvent) => {
if (e) {
const gcs = this.geoViewerRef.displayToGcs(e.map);
const p = this.pointAnnotation.data()[0];
const dx = Math.abs(gcs.x - p.x);
const dy = Math.abs(gcs.y - p.y);
if (Math.sqrt(dx*dx + dy*dy) < 20 || dy < 10) {
this.event('update:cursor', { cursor: 'grab' });
this.hovering = true;
return;
} else {
this.event('update:cursor', { cursor: 'default' });
}
}
this.hovering = false;
};
this.mousedownHandler = (e: GeoEvent) => {
if (this.hovering && e.buttons.left) {
this.geoViewerRef.interactor().addAction({
action: 'dragpoint',
name: 'drag point with mouse',
owner: 'MeasureToolLayer',
input: 'left',
});
this.dragging = true;
this.event('update:cursor', { cursor: 'grabbing' });
}
};
this.mouseupHandler = () => {
this.dragging = false;
this.geoViewerRef.interactor().removeAction(undefined, undefined, 'MeasureToolLayer');
this.updateRuler(this.yValue);
this.event('update:cursor', { cursor: 'grab' });
};
}

enableDrawing() {
this.rulerOn = true;
// Frequency ruler
this.lineAnnotation = this.frequencyRulerLayer.createFeature('line')
.data([[
{x: 0, y: this.yValue},
{x: this.spectroInfo.width, y: this.yValue},
]])
.style(this.createLineStyle());
this.pointAnnotation = this.frequencyRulerLayer.createFeature('point')
.data([{x: 0, y: this.yValue}])
.style(this.createPointStyle());
this.geoViewerRef.geoOn(geo.event.mousedown, this.mousedownHandler);
this.geoViewerRef.geoOn(geo.event.actionmove, this.moveHandler);
this.geoViewerRef.geoOn(geo.event.mouseup, this.mouseupHandler);
this.geoViewerRef.geoOn(geo.event.mousemove, this.hoverHandler);
this.frequencyRulerLayer.draw();
this.updateRuler(this.yValue);
}

_getTextCoordinates(): { x: number, y: number } {
const bounds = this.geoViewerRef.bounds();
const startX = 0;
const endX = ((this.compressedView
? this.scaledWidth
: this.spectroInfo.width
) || this.spectroInfo.width);
const left = Math.max(startX, bounds.left);
const right = Math.min(endX, bounds.right);
return { x: (left + right) / 2, y: this.yValue };
}

updateRuler(newY: number) {
if (newY < 0) {
return;
}
this.event("measure:dragged", { yValue: newY });
this.yValue = newY;
const spectroWidth = this.compressedView ? this.scaledWidth : this.spectroInfo.width;
this.lineAnnotation
.data([[
{x: 0, y: this.yValue},
{x: (spectroWidth || this.spectroInfo.width), y: this.yValue},
]])
.style(this.createLineStyle());
this.pointAnnotation
.data([{x: 0, y: this.yValue}])
.style(this.createPointStyle());
this.frequencyRulerLayer.draw();
const height = Math.max(this.scaledHeight, this.spectroInfo.height);
const frequency = height - this.yValue >= 0
? ((height - newY) * (this.spectroInfo.high_freq - this.spectroInfo.low_freq)) / height / 1000 + this.spectroInfo.low_freq / 1000
: -1;
const textValue = `${frequency.toFixed(1)}KHz`;
const { x: textX, y: textY } = this._getTextCoordinates();
this.textData = [
{
text: textValue,
x: textX,
y: textY,
offsetY: 20,
},
];
this.textLayer.data(this.textData).draw();
}

disableDrawing() {
this.rulerOn = false;
this.textData = [];
this.textLayer.data(this.textData).draw();
this.clearRulerLayer();
this.geoViewerRef.geoOff(geo.event.mousedown, this.mousedownHandler);
this.geoViewerRef.geoOff(geo.event.mouseup, this.mouseupHandler);
this.geoViewerRef.geoOff(geo.event.actionmove, this.moveHandler);
this.geoViewerRef.geoOff(geo.event.mousemove, this.hoverHandler);
}

clearRulerLayer() {
this.pointAnnotation?.data([]);
this.lineAnnotation?.data([]);
this.textLayer?.data([]).draw();
this.frequencyRulerLayer?.draw();
}

destroy() {
super.destroy();
this.textData = [];
if (this.frequencyRulerLayer) {
this.geoViewerRef.deleteLater(this.frequencyRulerLayer);
}
}

setScaledDimensions(scaledWidth: number, scaledHeight: number) {
// Get the frequency represented by the current ruler
const height = Math.max(this.scaledHeight, this.spectroInfo.height);
const frequency = height - this.yValue >= 0
? ((height - this.yValue) * (this.spectroInfo.high_freq - this.spectroInfo.low_freq)) / height / 1000 + this.spectroInfo.low_freq / 1000
: -1;
// Get the new yValue needed based on the updated scaled height
const newY = scaledHeight - ((frequency - (this.spectroInfo.low_freq / 1000)) * scaledHeight * 1000) / (this.spectroInfo.high_freq - this.spectroInfo.low_freq);
this.yValue = newY;
super.setScaledDimensions(scaledWidth, scaledHeight);
this.clearRulerLayer();
if (this.rulerOn) {
this.enableDrawing();
this.updateRuler(this.yValue);
}
}

redraw() {
if (this.rulerOn) {
this.updateRuler(this.yValue);
}
super.redraw();
}

createTextStyle(): LayerStyle<TextData> {
return {
color: () => _determineRulerColor(this.dragging, this.color === 'white'),
offset: (data: TextData) => ({
x: data.offsetX || 0,
y: data.offsetY || 0,
}),
textAlign: 'center',
textScaled: this.textScaled,
textBaseline: 'bottom',
};
}

createPointStyle(): LayerStyle<LineData> {
return {
radius: 10,
fillColor: () => _determineRulerColor(this.dragging, this.color === 'white'),
strokeColor: () => _determineRulerColor(this.dragging, this.color === 'white'),
stroke: true,
strokeWidth: 5,
};
}

createLineStyle(): LayerStyle<LineData> {
return {
strokeColor: () => _determineRulerColor(this.dragging, this.color === 'white'),
strokeWidth: 2,
};
}

}
Loading