From a964dfd742ccf17a56836c0a1ef9e26178cd241d Mon Sep 17 00:00:00 2001
From: Lewin Probst <info@emirror.de>
Date: Sat, 10 Aug 2019 17:35:54 +0200
Subject: [PATCH] Added timeOfArrival window, Plots are now self updating.

New features:
  + If one plot changes, all other opened plots will be redrawn to
    instantly see the changes that have been made.
  + Added first version of the TOA window.
  + Added menu entry for Time of arrival editing window in the main window.
---
 .../applyStatusToAllPointsExceptDeleted.m     |   2 +-
 .../+pressure/applyStatusToSelectedPoints.m   |   2 +-
 .../applyStatusToAllPointsExceptDeleted.m     |   2 +-
 .../applyStatusToSelectedPoints.m             |   2 +-
 .../applySoundSourceToSelectedPoints.m        |  50 ++++++
 .../applyStatusToSelectedPoints.m             |  35 +++++
 .../+controller/+edit/+timeOfArrival/close.m  |  18 +++
 .../+controller/+edit/+timeOfArrival/open.m   |  22 +++
 .../+edit/+timeOfArrival/pickPolygon.m        |  70 +++++++++
 .../+controller/+edit/+timeOfArrival/plot.m   | 145 ++++++++++++++++++
 .../removeSoundSourceFromSelectedPoints.m     |  36 +++++
 .../+controller/+edit/dataPointsSelected.m    |  18 +++
 .../+controller/+edit/updateAvailablePlots.m  |  17 ++
 lib/+artoa/+controller/soundSourcesLoaded.m   |  19 +++
 lib/+artoa/+gui/+edit/pressure.m              |   2 +-
 lib/+artoa/+gui/+edit/temperature.m           |   2 +-
 lib/+artoa/+gui/+edit/timeOfArrival.m         |  99 ++++++++++++
 lib/+artoa/+gui/main.m                        |   9 ++
 18 files changed, 544 insertions(+), 6 deletions(-)
 create mode 100644 lib/+artoa/+controller/+edit/+timeOfArrival/applySoundSourceToSelectedPoints.m
 create mode 100644 lib/+artoa/+controller/+edit/+timeOfArrival/applyStatusToSelectedPoints.m
 create mode 100644 lib/+artoa/+controller/+edit/+timeOfArrival/close.m
 create mode 100644 lib/+artoa/+controller/+edit/+timeOfArrival/open.m
 create mode 100644 lib/+artoa/+controller/+edit/+timeOfArrival/pickPolygon.m
 create mode 100644 lib/+artoa/+controller/+edit/+timeOfArrival/plot.m
 create mode 100644 lib/+artoa/+controller/+edit/+timeOfArrival/removeSoundSourceFromSelectedPoints.m
 create mode 100644 lib/+artoa/+controller/+edit/dataPointsSelected.m
 create mode 100644 lib/+artoa/+controller/+edit/updateAvailablePlots.m
 create mode 100644 lib/+artoa/+controller/soundSourcesLoaded.m
 create mode 100644 lib/+artoa/+gui/+edit/timeOfArrival.m

diff --git a/lib/+artoa/+controller/+edit/+pressure/applyStatusToAllPointsExceptDeleted.m b/lib/+artoa/+controller/+edit/+pressure/applyStatusToAllPointsExceptDeleted.m
index e41a86a..5708a0c 100644
--- a/lib/+artoa/+controller/+edit/+pressure/applyStatusToAllPointsExceptDeleted.m
+++ b/lib/+artoa/+controller/+edit/+pressure/applyStatusToAllPointsExceptDeleted.m
@@ -15,7 +15,7 @@ artoaWorkspace.editPressure.selectedPoints = (artoaWorkspace.statusPressure ~= 2
 artoaWorkspace.statusPressure(artoaWorkspace.editPressure.selectedPoints) = pStatus;
 
 %% Update gui
-artoa.controller.edit.pressure.plot();
+artoa.controller.edit.updateAvailablePlots();
 
 %% Remove the selected points from workspace
 artoaWorkspace.editPressure = rmfield(artoaWorkspace.editPressure, 'selectedPoints');
diff --git a/lib/+artoa/+controller/+edit/+pressure/applyStatusToSelectedPoints.m b/lib/+artoa/+controller/+edit/+pressure/applyStatusToSelectedPoints.m
index af9b814..7949351 100644
--- a/lib/+artoa/+controller/+edit/+pressure/applyStatusToSelectedPoints.m
+++ b/lib/+artoa/+controller/+edit/+pressure/applyStatusToSelectedPoints.m
@@ -24,7 +24,7 @@ end
 artoaWorkspace.statusPressure(artoaWorkspace.editPressure.selectedPoints) = pStatusCode;
 
 %% Update gui
-artoa.controller.edit.pressure.plot();
+artoa.controller.edit.updateAvailablePlots();
 delete(artoaGui.editPressure.selectedPolygon);
 artoaGui.editPressure = rmfield(artoaGui.editPressure, 'selectedPolygon');
 
diff --git a/lib/+artoa/+controller/+edit/+temperature/applyStatusToAllPointsExceptDeleted.m b/lib/+artoa/+controller/+edit/+temperature/applyStatusToAllPointsExceptDeleted.m
index a79323a..ddf702f 100644
--- a/lib/+artoa/+controller/+edit/+temperature/applyStatusToAllPointsExceptDeleted.m
+++ b/lib/+artoa/+controller/+edit/+temperature/applyStatusToAllPointsExceptDeleted.m
@@ -15,7 +15,7 @@ artoaWorkspace.editTemperature.selectedPoints = (artoaWorkspace.statusTemperatur
 artoaWorkspace.statusTemperature(artoaWorkspace.editTemperature.selectedPoints) = pStatus;
 
 %% Update gui
-artoa.controller.edit.temperature.plot();
+artoa.controller.edit.updateAvailablePlots();
 
 %% Remove the selected points from workspace
 artoaWorkspace.editTemperature = rmfield(artoaWorkspace.editTemperature, 'selectedPoints');
diff --git a/lib/+artoa/+controller/+edit/+temperature/applyStatusToSelectedPoints.m b/lib/+artoa/+controller/+edit/+temperature/applyStatusToSelectedPoints.m
index cf6ec69..ce21de6 100644
--- a/lib/+artoa/+controller/+edit/+temperature/applyStatusToSelectedPoints.m
+++ b/lib/+artoa/+controller/+edit/+temperature/applyStatusToSelectedPoints.m
@@ -24,7 +24,7 @@ end
 artoaWorkspace.statusTemperature(artoaWorkspace.editTemperature.selectedPoints) = pStatusCode;
 
 %% Update gui
-artoa.controller.edit.temperature.plot();
+artoa.controller.edit.updateAvailablePlots();
 delete(artoaGui.editTemperature.selectedPolygon);
 artoaGui.editTemperature = rmfield(artoaGui.editTemperature, 'selectedPolygon');
 
diff --git a/lib/+artoa/+controller/+edit/+timeOfArrival/applySoundSourceToSelectedPoints.m b/lib/+artoa/+controller/+edit/+timeOfArrival/applySoundSourceToSelectedPoints.m
new file mode 100644
index 0000000..77026a9
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/+timeOfArrival/applySoundSourceToSelectedPoints.m
@@ -0,0 +1,50 @@
+function [] = applySoundSourceToSelectedPoints()
+%APPLYSOUNDSOURCETOSELECTEDPOINTS Summary of this function goes here
+%   Detailed explanation goes here
+
+global artoaWorkspace artoaDataInput;
+
+%% Check if selection exists
+
+if ~artoa.controller.edit.dataPointsSelected(artoaWorkspace.editTimeOfArrival)
+    warndlg('You need to select data by using the "Pick" button first!', 'No data selected');
+    return;
+end
+
+%% Check if soundsources are loaded
+if ~artoa.controller.soundSourcesLoaded()
+    warndlg('You need to load a soundsource file first!', 'No sound sources loaded');
+    return;
+end
+
+%% Let the user select the sound source
+soundsources = fieldnames(artoaDataInput.soundsources);
+
+[index, success] = listdlg( ...
+    'PromptString', 'Soundsources:',...
+    'SelectionMode', 'single',...
+    'ListString', soundsources ...
+);
+
+if ~success
+    return;
+end
+
+%% Store 
+
+% update sound source of selected points
+artoaWorkspace.toaData.soundSource( ...
+    artoaWorkspace.editTimeOfArrival.selectedPoints ...
+) = soundsources(index);
+
+
+% Update status to selected
+artoa.controller.edit.timeOfArrival.applyStatusToSelectedPoints(1);
+
+%% Update plots
+artoa.controller.edit.updateAvailablePlots();
+%artoa.controller.edit.timeOfArrival.plot();
+
+
+end
+
diff --git a/lib/+artoa/+controller/+edit/+timeOfArrival/applyStatusToSelectedPoints.m b/lib/+artoa/+controller/+edit/+timeOfArrival/applyStatusToSelectedPoints.m
new file mode 100644
index 0000000..5d8a560
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/+timeOfArrival/applyStatusToSelectedPoints.m
@@ -0,0 +1,35 @@
+function [] = applyStatusToSelectedPoints(pStatusCode)
+%APPLYSTATUSTOSELECTEDPOINTS Applies the given status code to the workspace variables.
+%   Applies the given status to the workspace variable.
+%   If pUpdateGui is set to false, the polygon variables are not being
+%   cleaned up.
+%
+%   Parameters:
+%       pStatusCode (int)    The status code that will be applied to the
+%                            selected data.
+%
+
+global artoaWorkspace artoaGui;
+
+
+%% Check if selection exists
+
+if ~artoa.controller.edit.dataPointsSelected(artoaWorkspace.editTimeOfArrival)
+    warndlg('You need to select data by using the "Pick" button first!', 'No data selected');
+    return;
+end
+
+
+%% Set status
+artoaWorkspace.toaData.status(artoaWorkspace.editTimeOfArrival.selectedPoints) = pStatusCode;
+
+%% Update gui
+artoa.controller.edit.timeOfArrival.plot();
+delete(artoaGui.editTimeOfArrival.selectedPolygon);
+artoaGui.editTimeOfArrival = rmfield(artoaGui.editTimeOfArrival, 'selectedPolygon');
+
+%% Remove the polygon field from workspace
+artoaWorkspace.editTimeOfArrival = rmfield(artoaWorkspace.editTimeOfArrival, 'selectedPoints');
+
+end
+
diff --git a/lib/+artoa/+controller/+edit/+timeOfArrival/close.m b/lib/+artoa/+controller/+edit/+timeOfArrival/close.m
new file mode 100644
index 0000000..cdd558e
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/+timeOfArrival/close.m
@@ -0,0 +1,18 @@
+function [ ] = close()
+%CLOSEREQ Cleans up all variables that are used by the editTimeOfArrival GUI.
+%   Changes to the data is not being modified.
+
+global artoaGui artoaWorkspace;
+
+%% Close the figure
+
+delete(artoaGui.figures.editTimeOfArrival);
+
+%% Clean up variables
+
+artoaGui = rmfield(artoaGui, 'editTimeOfArrival');
+artoaGui.figures = rmfield(artoaGui.figures, 'editTimeOfArrival');
+artoaWorkspace = rmfield(artoaWorkspace, 'editTimeOfArrival');
+
+end
+
diff --git a/lib/+artoa/+controller/+edit/+timeOfArrival/open.m b/lib/+artoa/+controller/+edit/+timeOfArrival/open.m
new file mode 100644
index 0000000..aeac618
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/+timeOfArrival/open.m
@@ -0,0 +1,22 @@
+function [] = open()
+%OPEN Initializes the time of arrival editing gui.
+
+global artoaGui artoaWorkspace;
+
+%% Check if the gui is already opened
+
+if isfield(artoaGui.figures, 'editTimeOfArrival')
+    figure(artoaGui.figures.editTimeOfArrival);
+    return
+end
+
+
+%% Open the gui
+artoa.gui.edit.timeOfArrival();
+
+%% Create workspace variables
+artoaWorkspace.editTimeOfArrival = struct();
+
+
+end
+
diff --git a/lib/+artoa/+controller/+edit/+timeOfArrival/pickPolygon.m b/lib/+artoa/+controller/+edit/+timeOfArrival/pickPolygon.m
new file mode 100644
index 0000000..9021a4c
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/+timeOfArrival/pickPolygon.m
@@ -0,0 +1,70 @@
+function [] = pickPolygon()
+%PICKPOLYGON Lets the user pick a polygon in the plot.
+%   During selection, a line is being plotted that shows the selected
+%   points by the user.
+%   The polygon can be stopped by using the right mouse button. If
+%   finished, the selected points are being calculated and stored in the
+%   artoaWorkspace variable.
+
+global artoaGui artoaWorkspace;
+
+%% Check if polygon is already selected
+
+if artoa.controller.edit.dataPointsSelected(artoaWorkspace.editTimeOfArrival)
+    if strcmp(questdlg('Reset current line and pick a new one?', 'Confirmation', 'Yes', 'Cancel', 'Cancel'), 'Cancel')
+        return;
+    end
+    % reset the line and all required fields
+    delete(artoaGui.editTimeOfArrival.selectedPolygon);
+    artoaWorkspace.editTimeOfArrival = rmfield(artoaWorkspace.editTimeOfArrival, 'selectedPoints');
+end
+
+%% Select polygon by mouse
+x = [];
+y = [];
+
+% draw the line that will be extended on every click
+artoaGui.editTimeOfArrival.selectedPolygon = line( ...
+    NaN, NaN, 'Color', [1 1 1], 'LineStyle', '-' ...
+);
+
+[dx, dy, button] = ginput(1);
+while button == 1
+    % get boundaries
+    bound = artoaGui.figures.editTimeOfArrival.CurrentAxes.XLim;
+    dx = artoa.data.adjustToBoundaries(dx, bound(1), bound(2));
+    bound = artoaGui.figures.editTimeOfArrival.CurrentAxes.YLim;
+    dy = artoa.data.adjustToBoundaries(dy, bound(1), bound(2));
+    % add values to array
+    x = [x; dx];
+    y = [y; dy];
+    if length(x) == 1
+        continue;
+    end
+    % update the line
+    set(artoaGui.editTimeOfArrival.selectedPolygon, 'XData', x, 'YData', y);
+    [dx, dy, button] = ginput(1);
+end
+
+% close the poligon by adding the first value as the last one
+if length(x) > 3 % a polygon can only be closed if there are more than two values
+    x = [x; x(1)];
+    y = [y; y(1)];
+else
+    return;
+end
+% update the line
+set(artoaGui.editTimeOfArrival.selectedPolygon, 'XData', x, 'YData', y);
+
+% store the selected points in the workspace for editing
+artoaWorkspace.editTimeOfArrival.selectedPoints = inpolygon( ...
+    artoaWorkspace.toaData.toaDate, ...
+    artoaWorkspace.toaData.toa, ...
+    x, ...
+    y ...
+);
+
+
+
+end
+
diff --git a/lib/+artoa/+controller/+edit/+timeOfArrival/plot.m b/lib/+artoa/+controller/+edit/+timeOfArrival/plot.m
new file mode 100644
index 0000000..3440adf
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/+timeOfArrival/plot.m
@@ -0,0 +1,145 @@
+function [] = plot()
+%PLOT Plots or updates the time of arrival points.
+%   If the plot already exists and is valid, the points only get updated.
+%   Otherwise the plot will be initialized
+%
+
+global artoaGui artoaWorkspace artoaDataInput;
+
+hold on
+
+%% Get all required values
+% create color vector
+correlationHeights = artoaWorkspace.toaData.correlationHeight./artoaWorkspace.float.correlationrange(2);
+
+%c = repmat([.5 .5 .5], length(artoaWorkspace.toaData.toaDate), 1);
+c = correlationHeights * 0.4;
+
+soundsources = fieldnames(artoaDataInput.soundsources);
+for i = 1:length(soundsources)
+    selection = logical(artoaWorkspace.toaData.status == 1) ...
+        & logical(strcmp(artoaWorkspace.toaData.soundSource, soundsources{i}));
+    c(selection) = artoaWorkspace.soundSourceColorId(i) * 0.4 + 0.6;
+end
+%c(artoaWorkspace.toaData.status == 1) = correlationHeights(artoaWorkspace.toaData.status == 1) * 0.4 + 0.1;
+
+
+
+if artoaWorkspace.showAllDataPoints
+    x = artoaWorkspace.toaData.toaDate;
+    y = artoaWorkspace.toaData.toa;
+else
+    % get all toa that are considered valid by temperature and pressure
+    % selection
+    temperature = repmat(artoaWorkspace.statusTemperature, length(artoaDataInput.rfb.VARIABLE_LIST.time_of_arrival), 1);
+    pressure = repmat(artoaWorkspace.statusPressure, length(artoaDataInput.rfb.VARIABLE_LIST.time_of_arrival), 1);
+    selection = temperature == 1 & pressure == 1 & artoaWorkspace.toaData.status ~= 2;
+    x = artoaWorkspace.toaData.toaDate(selection);
+    y = artoaWorkspace.toaData.toa(selection);
+    c = c(selection, :);
+    clear temperature pressure selection;
+end
+
+%% If the plot exists, just update the values
+
+if isfield(artoaGui.editTimeOfArrival, 'scatterTimeOfArrival') ...
+        && isvalid(artoaGui.editTimeOfArrival.scatterTimeOfArrival)
+    artoaGui.editTimeOfArrival.scatterTimeOfArrival.XData = x;
+    artoaGui.editTimeOfArrival.scatterTimeOfArrival.YData = y;
+    artoaGui.editTimeOfArrival.scatterTimeOfArrival.CData = c;
+    return
+end
+
+%% Create a new plot if non existent
+
+% get all points that are not deleted
+artoaGui.editTimeOfArrival.scatterTimeOfArrival = scatter( ...
+    x, ...
+    y, ...
+    [], ...
+    c, ... %'.', 'MarkerSize', 10 ...
+    'filled' ...
+);
+
+% set colormap
+colormap(artoaGui.editTimeOfArrival.scatterTimeOfArrival.Parent, artoa.vendor.cmocean('tarn'));
+artoaGui.editTimeOfArrival.scatterTimeOfArrival.Parent.CLim = [0, 1];
+
+%% Setup GPS information plot
+    [gpsDates, predictedToas] = artoa.data.predictToaFromGps( ...
+        artoaDataInput.rfb, artoaDataInput.soundsources.W11e, ...
+        struct( ...
+            'temperature', artoaWorkspace.temperature(artoaWorkspace.statusTemperature == 1), ...
+            'pressure', artoaWorkspace.pressure(artoaWorkspace.statusPressure == 1), ...
+            'method', 'del grosso', ...
+            'soundSource', NaN ...
+        ) ...
+    );
+
+    scatter(gpsDates - 1, predictedToas, 20, 'MarkerFaceColor', [1 1 1]);
+    text(gpsDates, predictedToas, ['-', 'W11e'], ...¬                                                                                        
+        'HorizontalAlignment', 'left',...¬                               
+        'VerticalAlignment', 'top',...¬                                 
+        'FontName', 'Helvetica',...¬                                     
+        'FontSize', 10,...¬                                              
+        'FontWeight', 'demi',...¬                                        
+        'Color', [1 1 1] ...
+    );
+
+for o = 1:length(soundsources)
+    [gpsDates, predictedToas] = artoa.data.predictToaFromGps( ...
+        artoaDataInput.rfb, artoaDataInput.soundsources.(soundsources{o}), ...
+        struct( ...
+            'temperature', artoaWorkspace.temperature(artoaWorkspace.statusTemperature == 1), ...
+            'pressure', artoaWorkspace.pressure(artoaWorkspace.statusPressure == 1), ...
+            'method', 'del grosso', ...
+            'soundSource', NaN ...
+        ) ...
+    );
+
+    scatter(gpsDates, predictedToas, 20, 'MarkerFaceColor', [1 1 1]);
+    text(gpsDates, predictedToas, ['-', soundsources{o}], ...¬                                                                                        
+        'HorizontalAlignment', 'left',...¬                               
+        'VerticalAlignment', 'top',...¬                                 
+        'FontName', 'Helvetica',...¬                                     
+        'FontSize', 10,...¬                                              
+        'FontWeight', 'demi',...¬                                        
+        'Color', [1 1 1] ...
+    );
+    tmp = artoa.convert.dmy2rd(artoaDataInput.soundsources.(soundsources{o}).begemis(3), ...
+        artoaDataInput.soundsources.(soundsources{o}).begemis(2), ...
+        artoaDataInput.soundsources.(soundsources{o}).begemis(1));
+    plot([tmp tmp],[max(y) min(y)],'color', [1 .4 .4],'LineStyle','-.')
+    tmp = artoa.convert.dmy2rd(artoaDataInput.soundsources.(soundsources{o}).endemis(3), ...
+        artoaDataInput.soundsources.(soundsources{o}).endemis(2), ...
+        artoaDataInput.soundsources.(soundsources{o}).endemis(1));
+    plot([tmp tmp],[max(y) min(y)],'color', [1 .4 .4],'LineStyle','-.')
+end
+
+%% Setup gui
+
+grid on;
+
+% set background color
+set(gca, 'Color', [.3 .3 .3], 'XColor', 'blue', 'YColor', 'blue');
+
+% set plot title
+titleVal = [ ...
+    'Project: ' artoaWorkspace.float.projectname ...
+    ' FloatId: ' num2str(artoaWorkspace.float.floatname) ...
+    ' Cycle: ' num2str(artoaWorkspace.float.cycle(1)) ...
+    ' - Time of arrival' ...
+];
+titleHandle = title(titleVal, 'FontSize', 10);
+set(titleHandle, 'Color', 'blue');
+
+% set x and y label
+xlabel('Message Date', 'FontSize', 10);
+ylabel('Time of arrival', 'FontSize', 10);
+
+hold off
+
+
+
+end
+
diff --git a/lib/+artoa/+controller/+edit/+timeOfArrival/removeSoundSourceFromSelectedPoints.m b/lib/+artoa/+controller/+edit/+timeOfArrival/removeSoundSourceFromSelectedPoints.m
new file mode 100644
index 0000000..5f8669e
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/+timeOfArrival/removeSoundSourceFromSelectedPoints.m
@@ -0,0 +1,36 @@
+function [] = removeSoundSourceFromSelectedPoints()
+%REMOVESOUNDSOURCEFROMSELECTEDPOINTS Removes the previously picked points from the soundsource.
+
+global artoaWorkspace;
+
+%% Check if selection exists
+
+if ~artoa.controller.edit.dataPointsSelected(artoaWorkspace.editTimeOfArrival)
+    warndlg('You need to select data by using the "Pick" button first!', 'No data selected');
+    return;
+end
+
+%% Check if soundsources are loaded
+if ~artoa.controller.soundSourcesLoaded()
+    warndlg('You need to load a soundsource file first!', 'No sound sources loaded');
+    return;
+end
+
+%% Remove the connection
+
+% update sound source of selected points
+artoaWorkspace.toaData.soundSource( ...
+    artoaWorkspace.editTimeOfArrival.selectedPoints ...
+) = {''};
+
+
+% Update status to selected
+artoa.controller.edit.timeOfArrival.applyStatusToSelectedPoints(0);
+
+%% Update plot
+artoa.controller.edit.updateAvailablePlots();
+%artoa.controller.edit.timeOfArrival.plot();
+
+
+end
+
diff --git a/lib/+artoa/+controller/+edit/dataPointsSelected.m b/lib/+artoa/+controller/+edit/dataPointsSelected.m
new file mode 100644
index 0000000..d126a99
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/dataPointsSelected.m
@@ -0,0 +1,18 @@
+function [dataIsSelected] = dataPointsSelected(pHandle)
+%DATAPOINTSSELECTED Checks if data points have been selected by using the pick button.
+%   Returns true if the polygon exists, false otherwise.
+
+%% Setup return variable
+dataIsSelected = false;
+
+%% Check required fields
+if ~isfield(pHandle, 'selectedPoints') || isempty(pHandle.selectedPoints)
+    return;
+end
+
+%% Update return variable
+dataIsSelected = true;
+
+
+end
+
diff --git a/lib/+artoa/+controller/+edit/updateAvailablePlots.m b/lib/+artoa/+controller/+edit/updateAvailablePlots.m
new file mode 100644
index 0000000..4df49fc
--- /dev/null
+++ b/lib/+artoa/+controller/+edit/updateAvailablePlots.m
@@ -0,0 +1,17 @@
+function [] = updateAvailablePlots()
+%UPDATEAVAILABLEPLOTS Replots all opened edit plots.
+
+global artoaGui;
+
+plotsToUpdate = {'editTemperature', 'editPressure', 'editTimeOfArrival'};
+controllerPackageNames = {'temperature', 'pressure', 'timeOfArrival'};
+
+for i = 1:length(plotsToUpdate)
+    if ~isfield(artoaGui, plotsToUpdate{i})
+        continue;
+    end
+    artoa.controller.edit.(controllerPackageNames{i}).plot();
+end
+
+end
+
diff --git a/lib/+artoa/+controller/soundSourcesLoaded.m b/lib/+artoa/+controller/soundSourcesLoaded.m
new file mode 100644
index 0000000..21a6eff
--- /dev/null
+++ b/lib/+artoa/+controller/soundSourcesLoaded.m
@@ -0,0 +1,19 @@
+function [soundSourcesAreLoaded] = soundSourcesLoaded()
+%SOUNDSOURCESLOADED Checks if a sound source file has been loaded.
+%   Returns true if sound sources are available.
+
+global artoaDataInput;
+
+%% Setup return variable
+soundSourcesAreLoaded = false;
+
+%% Check if required field is available
+if ~isfield(artoaDataInput, 'soundsources') && ~isempty(artoaDataInput.soundsources)
+    return;
+end
+
+%% Update return variables
+soundSourcesAreLoaded = true;
+
+end
+
diff --git a/lib/+artoa/+gui/+edit/pressure.m b/lib/+artoa/+gui/+edit/pressure.m
index f412986..e2772dd 100644
--- a/lib/+artoa/+gui/+edit/pressure.m
+++ b/lib/+artoa/+gui/+edit/pressure.m
@@ -26,7 +26,7 @@ set( ...
 
 %% Generate Plot
 
-artoa.controller.edit.pressure.plot();
+artoa.controller.edit.updateAvailablePlots();
 
 set(gca, 'Position', [0.13 0.11 0.706 0.815]);
 
diff --git a/lib/+artoa/+gui/+edit/temperature.m b/lib/+artoa/+gui/+edit/temperature.m
index ed687d3..a4e0f02 100644
--- a/lib/+artoa/+gui/+edit/temperature.m
+++ b/lib/+artoa/+gui/+edit/temperature.m
@@ -26,7 +26,7 @@ set( ...
 
 %% Generate Plot
 
-artoa.controller.edit.temperature.plot();
+artoa.controller.edit.updateAvailablePlots();
 
 set(gca, 'Position', [0.13 0.11 0.706 0.815]);
 
diff --git a/lib/+artoa/+gui/+edit/timeOfArrival.m b/lib/+artoa/+gui/+edit/timeOfArrival.m
new file mode 100644
index 0000000..7f5a8a6
--- /dev/null
+++ b/lib/+artoa/+gui/+edit/timeOfArrival.m
@@ -0,0 +1,99 @@
+function [] = timeOfArrival()
+%TIMEOFARRIVAL Defines the ARTOA4 edit time of arrival window.
+
+global artoaGui artoaWorkspace;
+
+%% Initialize required variables
+
+windowTitle = [ 'ARTOA4 - Float ' num2str(artoaWorkspace.float.floatname) ' - Time of arrival' ];
+
+
+%% Initialize time of arrival gui
+
+artoaGui.figures.editTimeOfArrival = figure( ...
+    'Name', windowTitle, ...
+    'NumberTitle', 'off' ...
+);
+addToolbarExplorationButtons(artoaGui.figures.editTimeOfArrival);
+
+artoaGui.editTimeOfArrival = struct();
+
+set( ...
+    artoaGui.figures.editTimeOfArrival, ...
+    'CloseRequestFcn', ...
+    'artoa.controller.edit.timeOfArrival.close();' ...
+);
+
+%% Generate Plot
+
+artoa.controller.edit.timeOfArrival.plot();
+
+set(gca, 'Position', [0.13 0.11 0.706 0.815]);
+
+%% Generate Controls
+
+left = .85;
+width = .14;
+buttonHeight = .05;
+buttonMarginLeftRight = .005;
+
+%% Data frame
+artoaGui.editTimeOfArrival.frameControls = uipanel( ...
+    'Title', 'Data', ...
+    'Units', 'normalized', ...
+    'BackgroundColor', 'white', ...
+    'Position', [left .6 width .3] ...
+);
+
+artoaGui.editTimeOfArrival.buttonPick = uicontrol( ...
+    'Parent', artoaGui.editTimeOfArrival.frameControls, ...
+    'String', 'Pick', ...
+    'Style', 'PushButton', ...
+    'FontSize', 8, ...
+    'Units', 'normalized', ...
+    'Position', [.156 .75 .69 .2], ...
+    'CallBack', 'artoa.controller.edit.timeOfArrival.pickPolygon();' ...
+);
+
+artoaGui.editTimeOfArrival.buttonReplot = uicontrol( ...
+    'Parent', artoaGui.editTimeOfArrival.frameControls, ...
+    'String', 'Replot', ...
+    'Style', 'PushButton', ...
+    'FontSize', 8, ...
+    'Units', 'normalized', ...
+    'Position', [.156 .05 .69 .2], ...
+    'CallBack', 'artoa.controller.edit.timeOfArrival.plot();' ...
+);
+
+%% Sound source frame
+
+artoaGui.editTimeOfArrival.frameControlsSoundSource = uipanel( ...
+    'Title', 'Soundsource', ...
+    'Units', 'normalized', ...
+    'BackgroundColor', 'white', ...
+    'Position', [left .11 width .2] ...
+);
+
+artoaGui.editTimeOfArrival.buttonApply = uicontrol( ...
+    'Parent', artoaGui.editTimeOfArrival.frameControlsSoundSource, ...
+    'String', 'Apply', ...
+    'Style', 'PushButton', ...
+    'FontSize', 8, ...
+    'Units', 'normalized', ...
+    'Position', [.156 .55 .69 .4], ...
+    'CallBack', 'artoa.controller.edit.timeOfArrival.applySoundSourceToSelectedPoints();' ...
+);
+
+artoaGui.editTimeOfArrival.buttonDelete = uicontrol( ...
+    'Parent', artoaGui.editTimeOfArrival.frameControlsSoundSource, ...
+    'String', 'Withdraw', ...
+    'Style', 'PushButton', ...
+    'FontSize', 8, ...
+    'Units', 'normalized', ...
+    'Position', [.156 .05 .69 .4], ...
+    'CallBack', 'artoa.controller.edit.timeOfArrival.removeSoundSourceFromSelectedPoints();' ...
+);
+
+
+end
+
diff --git a/lib/+artoa/+gui/main.m b/lib/+artoa/+gui/main.m
index d0041c1..4e54a7f 100644
--- a/lib/+artoa/+gui/main.m
+++ b/lib/+artoa/+gui/main.m
@@ -41,6 +41,7 @@ uimenu( ...
     'Callback', 'artoa.controller.loadSoundSourceFile();' ...
 );
 
+
 % SAVE
 saveHandle = uimenu(artoaGui.main.menus.file, 'Label', 'Save');
 uimenu( ...
@@ -88,6 +89,14 @@ artoaGui.main.menus.editPressure = uimenu( ...
     'Callback', 'artoa.controller.edit.pressure.open();' ...
 );
 
+% TIME OF ARRIVAL
+artoaGui.main.menus.editTimeOfArrival = uimenu( ...
+    artoaGui.main.menus.edit, ...
+    'Label', 'Time of arrival', ...
+    'Callback', 'artoa.controller.edit.timeOfArrival.open();', ...
+    'Separator', 'on' ...
+);
+
 %% Initialize view menu
 
 artoaGui.main.menus.view = uimenu( ...
-- 
GitLab