diff --git a/FlowManager/displayManager.m b/FlowManager/displayManager.m index 63b0055f2..2d5d2a051 100644 --- a/FlowManager/displayManager.m +++ b/FlowManager/displayManager.m @@ -111,7 +111,6 @@ function displayManager(windowTitle, sample_data, callbacks) end lastState = ''; - lastSetIdx = []; qcSet = str2double(readProperty('toolbox.qc_set')); badFlag = imosQCFlag('bad', qcSet, 'flag'); @@ -125,7 +124,7 @@ function displayManager(windowTitle, sample_data, callbacks) mainWindow(windowTitle, sample_data, states, 3, @stateSelectCallback); function state = stateSelectCallback(event,... - panel, updateCallback, state, sample_data, graphType, setIdx, vars) + panel, updateCallback, state, sample_data, graphType, setIdx, vars, extraSetIdx) %STATESELECTCALLBACK Called when the user interacts with the main % window, by changing the state, the selected data set, the selected % variables or the selected graph type. @@ -139,6 +138,7 @@ function displayManager(windowTitle, sample_data, callbacks) % graphType - currently selected graph type (string). % setIdx - currently selected sample_data struct (index) % vars - currently selected variables (indices). + % extraSetIdx - currently selected extra sample_data struct (index) % % Outputs: % state - If a state change was forced, tells the main window @@ -157,7 +157,6 @@ function displayManager(windowTitle, sample_data, callbacks) end lastState = state; - lastSetIdx = setIdx; function importCallback() %IMPORTCALLBACK Called when the user clicks the 'Import' button. Calls @@ -248,12 +247,18 @@ function rawDataCallback() nVar = length(sample_data{setIdx}.variables) - 3; end vars(vars > nVar) = []; + + if extraSetIdx + extra_sample_data = sample_data{extraSetIdx}; + else + extra_sample_data = []; + end % display selected raw data graphs = []; try graphFunc = getGraphFunc(graphType, 'graph', ''); - [graphs, lines, vars] = graphFunc(panel, sample_data{setIdx}, vars); + [graphs, lines, vars] = graphFunc(panel, sample_data{setIdx}, vars, extra_sample_data); catch e errorString = getErrorString(e); fprintf('%s\n', ['Error says : ' errorString]); @@ -268,7 +273,6 @@ function rawDataCallback() % so the data select callback can retrieve them if ~isempty(graphs) for k = 1:length(graphs) - set(graphs(k), 'UserData', {lines(k,:), k}); end end @@ -326,6 +330,12 @@ function qcDataCallback() for k = 1:length(sample_data), updateCallback(sample_data{k}); end end + + if extraSetIdx + extra_sample_data = sample_data{extraSetIdx}; + else + extra_sample_data = []; + end % redisplay the data try @@ -335,7 +345,7 @@ function qcDataCallback() flagFunc = []; end - [graphs, lines, vars] = graphFunc(panel, sample_data{setIdx}, vars); + [graphs, lines, vars] = graphFunc(panel, sample_data{setIdx}, vars, extra_sample_data); if isempty(flagFunc) warning(['Cannot display QC flags using ' graphType ... @@ -637,7 +647,7 @@ function exportNetCDFCallback() callbacks.exportNetCDFRequestCallback(); stateSelectCallback('', panel, updateCallback, lastState, ... - sample_data, graphType, setIdx, vars); + sample_data, graphType, setIdx, vars, extraSetIdx); state = lastState; end @@ -648,7 +658,7 @@ function exportRawCallback() callbacks.exportRawRequestCallback(); stateSelectCallback('', panel, updateCallback, lastState, ... - sample_data, graphType, setIdx, vars); + sample_data, graphType, setIdx, vars, extraSetIdx); state = lastState; end end diff --git a/GUI/mainWindow.m b/GUI/mainWindow.m index 5ecce1843..bc94a3575 100644 --- a/GUI/mainWindow.m +++ b/GUI/mainWindow.m @@ -1,5 +1,4 @@ -function mainWindow(... - windowTitle, sample_data, states, startState, selectionCallback) +function mainWindow(windowTitle, sample_data, states, startState, selectionCallback) %MAINWINDOW Displays a window which allows the user to view data. % % The mainWindow is the main toolbox window. It provides menus allowing the @@ -11,14 +10,14 @@ function mainWindow(... % up to the calling function to define the buttons which will appear down % the left of the window; these are referred to as states. % -% Whenever the user changes the state (via a state button) or any of the -% options, the provided selectionCallback function is called with the new +% Whenever the user changes the state (via a state button) or any of the +% options, the provided selectionCallback function is called with the new % selection. % % The main window never modifies data, but must be kept informed of changes % to the data set. This is achieved by the updateCallback function handle % which is passed to the selectionCallback function. Whenever data is -% changed, this function must be called in order to keep the main window +% changed, this function must be called in order to keep the main window % display consistent. % % Inputs: @@ -26,13 +25,13 @@ function mainWindow(... % sample_data - Cell array of structs of sample data. % states - Cell array of strings containing state names. % startState - Index into states array, specifying initial state. -% selectionCallback - A function which is called when the user pushes a -% state button. The function must take the following +% selectionCallback - A function which is called when the user pushes a +% state button. The function must take the following % input arguments: % event - String describing what triggered % the callback. One of 'state', % 'set', 'graph', or 'var'. -% panel - A uipanel on which things can be +% panel - A uipanel on which things can be % drawn. % updateCallback - Function to be called when data % is updated. The function is of @@ -42,72 +41,70 @@ function mainWindow(... % state - Currently selected state (String) % sample_data - Cell array of sample_data % structs -% graphType - Currently selected graph type +% graphType - Currently selected graph type % (String) % set - Currently selected sample_data % struct % vars - Currently selected variables % The function must also provide one output argument: % state - the new state, if it changed. -% +% % % Author: Paul McCarthy % Contributor: Guillaume Galibert % % -% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated % Marine Observing System (IMOS). % All rights reserved. -% -% Redistribution and use in source and binary forms, with or without +% +% Redistribution and use in source and binary forms, with or without % modification, are permitted provided that the following conditions are met: -% -% * Redistributions of source code must retain the above copyright notice, +% +% * Redistributions of source code must retain the above copyright notice, % this list of conditions and the following disclaimer. -% * Redistributions in binary form must reproduce the above copyright -% notice, this list of conditions and the following disclaimer in the +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the % documentation and/or other materials provided with the distribution. -% * Neither the name of the AODN/IMOS nor the names of its contributors -% may be used to endorse or promote products derived from this software +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software % without specific prior written permission. -% -% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(5,5); +narginchk(5,5); - if ~ischar(windowTitle), error('windowTitle must be a string'); end - if ~iscell(sample_data)... - || isempty(sample_data), error('sample_data must be a cell array'); end - if ~iscellstr(states), error('states must be a cell array'); end - if ~isnumeric(startState), error('initialState must be a numeric'); end - if ~isa(selectionCallback,... - 'function_handle'), error('selectionCallback must be a function'); end - - currentState = states{startState}; - - % sample menu entries (1-1 mapping to sample_data structs) - lenSam = length(sample_data); - sampleDataDescs = cell(lenSam, 1); - for k = 1:lenSam +if ~ischar(windowTitle), error('windowTitle must be a string'); end +if ~iscell(sample_data) || isempty(sample_data), error('sample_data must be a cell array'); end +if ~iscellstr(states), error('states must be a cell array'); end +if ~isnumeric(startState), error('initialState must be a numeric'); end +if ~isa(selectionCallback, 'function_handle'), error('selectionCallback must be a function'); end + +currentState = states{startState}; + +% sample menu entries (1-1 mapping to sample_data structs) +lenSam = length(sample_data); +sampleDataDescs = cell(lenSam, 1); +for k = 1:lenSam sampleDataDescs{k} = genSampleDataDesc(sample_data{k}); - end +end - % window figure - fig = figure(... +% window figure +fig = figure(... 'Name', windowTitle, ... 'Visible', 'off',... - 'Color', [0.92549 0.913725 0.847059],... + 'Color', [0.75 0.75 0.75],... 'MenuBar', 'none',... 'ToolBar', 'figure',... 'Resize', 'on',... @@ -115,614 +112,502 @@ function mainWindow(... 'NumberTitle', 'off',... 'Tag', 'mainWindow'); - % sample data selection menu - sampleMenu = uicontrol(... +% sample data selection menu +sampleMenu = uicontrol(... 'Style', 'popupmenu',... 'String', sampleDataDescs,... 'Value', 1,... - 'Tag', 'samplePopUpMenu'); - - % get the toolbox execution mode - mode = readProperty('toolbox.mode'); - - switch mode - case 'profile' - graphMenuValue = 2; - case 'timeSeries' - graphMenuValue = 1; - end + 'Tag', 'samplePopUpMenu'); - % graph type selection menu - graphMenu = uicontrol(... +% get the toolbox execution mode +mode = readProperty('toolbox.mode'); + +switch mode + case 'profile' + graphMenuValue = 2; + case 'timeSeries' + graphMenuValue = 1; +end + +% graph type selection menu +graphMenu = uicontrol(... 'Style', 'popupmenu',... 'String', listGraphs(),... 'Value', graphMenuValue); - - % side panel - sidePanel = uipanel(... + +% extra instrument check box +extraSampleCb = uicontrol(... + 'Style', 'checkbox',... + 'String', 'Graph extra instrument (black)',... + 'Value', 0,... + 'Tag', 'extraSampleCheckBox'); + +% extra sample data selection menu +extraSampleMenu = uicontrol(... + 'Style', 'popupmenu',... + 'String', sampleDataDescs,... + 'Value', 1,... + 'Enable', 'off',... + 'Tag', 'extraSamplePopUpMenu'); + +% side panel +sidePanel = uipanel(... 'Parent', fig,... 'BorderType', 'none'); - - % state buttons - lenStates = length(states); - stateButtons = nan(lenStates, 1); - for k = 1:lenStates + +% buttons panel +butPanel = uipanel(... + 'Parent', sidePanel,... + 'BorderType', 'none'); + +% state buttons +lenStates = length(states); +stateButtons = nan(lenStates, 1); +for k = 1:lenStates stateButtons(k) = uicontrol(... - 'Parent', sidePanel,... - 'Style', 'pushbutton',... - 'String', states{k}); - end - - % button to save current graph as an image - graphButton = uicontrol(... - 'Parent', sidePanel, ... - 'Style', 'pushbutton',... - 'String', 'Save Graph' ... - ); - - % variable selection panel - created in createVarPanel - varPanel = uipanel(... + 'Parent', butPanel,... + 'Style', 'pushbutton',... + 'String', states{k}); +end + +% variable selection panel - created in createVarPanel +varPanel = uipanel(... 'Parent', sidePanel,... 'BorderType', 'none'); - - % main display - mainPanel = uipanel(... + +% main display +mainPanel = uipanel(... 'Parent', fig,... 'BorderType', 'none',... - 'Tag', 'mainPanel'); - - % use normalized units - set(fig, 'Units', 'normalized'); - set(sidePanel, 'Units', 'normalized'); - set(mainPanel, 'Units', 'normalized'); - set(varPanel, 'Units', 'normalized'); - set(graphButton, 'Units', 'normalized'); - set(sampleMenu, 'Units', 'normalized'); - set(graphMenu, 'Units', 'normalized'); - set(stateButtons, 'Units', 'normalized'); - - % set window position - set(fig, 'Position', [0.1, 0.15, 0.8, 0.7 ]); - - % restrict window to primary screen - set(fig, 'Units', 'pixels'); - pos = get(fig, 'OuterPosition'); - monitors = get(0, 'MonitorPositions'); - if pos(3) > monitors(1,3) - pos(1) = 1; - pos(3) = monitors(1,3); - set(fig, 'OuterPosition', pos); - get(fig, 'Position'); - end - - % set widget positions - set(sidePanel, 'Position', [0.0, 0.0, 0.15, 0.95]); - set(mainPanel, 'Position', [0.15, 0.0, 0.85, 0.95]); - set(sampleMenu, 'Position', [0.0, 0.95, 0.75, 0.05]); - set(graphMenu, 'Position', [0.75, 0.95, 0.25, 0.05]); - - set(fig, 'Units', 'normalized'); + 'Tag', 'mainPanel'); - % set widget positions -% set(sidePanel, 'Position', [0.0, 0.0, 0.15, 0.95]); - set(sidePanel, 'Position', posUi2(fig, 100, 100, 6:100, 1:10, 0)); -% set(mainPanel, 'Position', [0.15, 0.0, 0.85, 0.95]); - set(mainPanel, 'Position', posUi2(fig, 100, 100, 6:100, 11:100, 0)); -% set(sampleMenu, 'Position', [0.0, 0.95, 0.75, 0.05]); - set(sampleMenu, 'Position', posUi2(fig, 100, 100, 1:5, 1:75, 0)); -% set(graphMenu, 'Position', [0.75, 0.95, 0.25, 0.05]); - set(graphMenu, 'Position', posUi2(fig, 100, 100, 1:5, 76:100, 0)); - - % varPanel, graph and stateButtons are positioned relative to sidePanel -% set(varPanel, 'Position', [0.0, 0.0, 1.0, 0.5]); - set(varPanel, 'Position', posUi2(sidePanel, 10, 1, 6:10, 1, 0)); - - n = length(stateButtons); - for k = 1:n -% set(stateButtons(k), 'Position', ... -% [0.0, 0.5+(n+1-k)*(0.5/(n+1)), 1.0, 0.5/(n+1)]); - set(stateButtons(k), 'Position', ... - posUi2(sidePanel, 2*(n+1)+1, 1, k, 1, 0)); - end - - % graph button is tacked on right below state buttons -% set(graphButton, 'Position', [0.0, 0.5, 1.0, 0.5/(n+1)]); - set(graphButton, 'Position', posUi2(sidePanel, 2*(n+1)+1, 1, n+1, 1, 0)); - - % reset back to pixels -% set(fig, 'Units', 'pixels'); -% set(sidePanel, 'Units', 'pixels'); -% set(mainPanel, 'Units', 'pixels'); -% set(varPanel, 'Units', 'pixels'); -% set(graphButton, 'Units', 'pixels'); -% set(sampleMenu, 'Units', 'pixels'); -% set(graphMenu, 'Units', 'pixels'); -% set(stateButtons, 'Units', 'pixels'); - - % set callbacks - variable panel widget - % callbacks are set in createParamPanel - set(sampleMenu, 'Callback', @sampleMenuCallback); - set(graphMenu, 'Callback', @graphMenuCallback); - set(stateButtons, 'Callback', @stateButtonCallback); - set(graphButton, 'Callback', @graphButtonCallback); - - set(fig, 'Visible', 'on'); - createVarPanel(sample_data{1}, []); - selectionChange('state'); - - tb = findall(fig, 'Type', 'uitoolbar'); - buttons = findall(tb); - - zoomoutb = findobj(buttons, 'TooltipString', 'Zoom Out'); - zoominb = findobj(buttons, 'TooltipString', 'Zoom In'); - panb = findobj(buttons, 'TooltipString', 'Pan'); - datacursorb = findobj(buttons, 'TooltipString', 'Data Cursor'); - - buttons(buttons == tb) = []; - buttons(buttons == zoomoutb) = []; - buttons(buttons == zoominb) = []; - buttons(buttons == panb) = []; - buttons(buttons == datacursorb) = []; - - delete(buttons); - - %set zoom/pan post-callback - %zoom v6 off; % undocumented Matlab to make sure zoom function prior to R14 is not used. Seems to not be supported from R2015a. - hZoom = zoom(fig); - hPan = pan(fig); - set(hZoom, 'ActionPostCallback', @zoomPostCallback); - set(hPan, 'ActionPostCallback', @zoomPostCallback); - - % update zoom in and pan's tooltips to display hot keys - set(zoominb, 'TooltipString', 'Zoom In (Ctrl+z)'); - set(panb, 'TooltipString', 'Pan (Ctrl+a)'); - - %set uimenu - hToolsMenu = uimenu(fig, 'label', 'Tools'); - switch mode - case 'timeSeries' - hToolsCheckPlannedDepths = uimenu(hToolsMenu, 'label', 'Check measured against planned depths'); - hToolsCheckPlannedDepthsNonQC = uimenu(hToolsCheckPlannedDepths, 'label', 'all data'); - hToolsCheckPlannedDepthsQC = uimenu(hToolsCheckPlannedDepths, 'label', 'only good and non QC''d data'); - hToolsCheckPressDiffs = uimenu(hToolsMenu, 'label', 'Check pressure differences between selected instrument and nearest neighbours'); - hToolsCheckPressDiffsNonQC = uimenu(hToolsCheckPressDiffs, 'label', 'all data'); - hToolsCheckPressDiffsQC = uimenu(hToolsCheckPressDiffs, 'label', 'only good and non QC''d data'); - hToolsLineDepth = uimenu(hToolsMenu, 'label', 'Line plot mooring''s depths'); - hToolsLineDepthNonQC = uimenu(hToolsLineDepth, 'label', 'all data'); - hToolsLineDepthQC = uimenu(hToolsLineDepth, 'label', 'only good and non QC''d data'); - hToolsLineCommonVar = uimenu(hToolsMenu, 'label', 'Line plot mooring''s 1D variables'); - hToolsLineCommonVarNonQC = uimenu(hToolsLineCommonVar, 'label', 'all data'); - hToolsLineCommonVarQC = uimenu(hToolsLineCommonVar, 'label', 'only good and non QC''d data'); - hToolsScatterCommonVar = uimenu(hToolsMenu, 'label', 'Scatter plot mooring''s 1D variables VS depth'); - hToolsScatterCommonVarNonQC = uimenu(hToolsScatterCommonVar, 'label', 'all data'); - hToolsScatterCommonVarQC = uimenu(hToolsScatterCommonVar, 'label', 'only good and non QC''d data'); - hToolsScatter2DCommonVar = uimenu(hToolsMenu, 'label', 'Scatter plot mooring''s 2D variables VS depth'); - hToolsScatter2DCommonVarNonQC = uimenu(hToolsScatter2DCommonVar, 'label', 'all data'); - hToolsScatter2DCommonVarQC = uimenu(hToolsScatter2DCommonVar, 'label', 'only good and non QC''d data'); - - %set menu callbacks - set(hToolsCheckPlannedDepthsNonQC, 'callBack', {@displayCheckPlannedDepths, false}); - set(hToolsCheckPlannedDepthsQC, 'callBack', {@displayCheckPlannedDepths, true}); - set(hToolsCheckPressDiffsNonQC, 'callBack', {@displayCheckPressDiffs, false}); - set(hToolsCheckPressDiffsQC, 'callBack', {@displayCheckPressDiffs, true}); - set(hToolsLineDepthNonQC, 'callBack', {@displayLineMooringDepth, false}); - set(hToolsLineDepthQC, 'callBack', {@displayLineMooringDepth, true}); - set(hToolsLineCommonVarNonQC, 'callBack', {@displayLineMooringVar, false}); - set(hToolsLineCommonVarQC, 'callBack', {@displayLineMooringVar, true}); - set(hToolsScatterCommonVarNonQC, 'callBack', {@displayScatterMooringVar, false, true}); - set(hToolsScatterCommonVarQC, 'callBack', {@displayScatterMooringVar, true, true}); - set(hToolsScatter2DCommonVarNonQC, 'callBack', {@displayScatterMooringVar, false, false}); - set(hToolsScatter2DCommonVarQC, 'callBack', {@displayScatterMooringVar, true, false}); - case 'profile' - hToolsLineCastVar = uimenu(hToolsMenu, 'label', 'Line plot profile variables'); - hToolsLineCastVarNonQC = uimenu(hToolsLineCastVar, 'label', 'all data'); - hToolsLineCastVarQC = uimenu(hToolsLineCastVar, 'label', 'only good and non QC''d data'); - - %set menu callbacks - set(hToolsLineCastVarNonQC, 'callBack', {@displayLineCastVar, false}); - set(hToolsLineCastVarQC, 'callBack', {@displayLineCastVar, true}); - end - hHotKeyMenu = uimenu(fig, 'label', 'Hot Keys'); - uimenu(hHotKeyMenu, 'Label', 'Enable zoom', 'Accelerator', 'z', 'Callback', @(src,evt)zoom(fig, 'on')); - uimenu(hHotKeyMenu, 'Label', 'Enable pan', 'Accelerator', 'a', 'Callback', @(src,evt)pan( fig, 'on')); - uimenu(hHotKeyMenu, 'Label', 'Disable zoom/pan', 'Accelerator', 'q', 'Callback', @disableZoomAndPan); +% use normalized units +set(fig, 'Units', 'normalized'); +set(sidePanel, 'Units', 'normalized'); +set(mainPanel, 'Units', 'normalized'); +set(butPanel, 'Units', 'normalized'); +set(varPanel, 'Units', 'normalized'); +set(sampleMenu, 'Units', 'normalized'); +set(graphMenu, 'Units', 'normalized'); +set(extraSampleCb, 'Units', 'normalized'); +set(extraSampleMenu, 'Units', 'normalized'); +set(stateButtons, 'Units', 'normalized'); - hHelpMenu = uimenu(fig, 'label', 'Help'); - hHelpWiki = uimenu(hHelpMenu, 'label', 'IMOS Toolbox Wiki'); - - %set menu callbacks - set(hHelpWiki, 'callBack', @openWikiPage); - - %% Widget Callbacks - - function selectionChange(event) - %Retrieves the current selection, clears the main panel, and calls - % selectionCallback. - % - state = currentState; - sam = getSelectedData(); - vars = getSelectedVars(); - graph = getSelectedGraphType(); - - % clear main panel - children = get(mainPanel, 'Children'); - delete(children); - - % delete any mouse listeners that may have been added - zoom off; - pan off; - set(fig, 'WindowButtonDownFcn', []); - set(fig, 'WindowButtonMotionFcn', []); - set(fig, 'WindowButtonUpFcn', []); - - currentState = selectionCallback(... - event,... - mainPanel, ... - @updateCallback, ... - state, ... - sample_data, ... - graph, ... - sam.meta.index, vars); - - % set data cursor mode custom display - dcm_obj = datacursormode(fig); - set(dcm_obj, 'UpdateFcn', {@customDcm, sam, vars, graph, mode}); - end - - function sampleMenuCallback(source,ev) - %SAMPLEMENUCALLBACK Called when a dataset is selected from the sample - % menu. Updates the variables panel, then delegates to selectionChange. - % - sam = getSelectedData(); - - % keep track of previously selected variables - iTickBox = getSelectedVars(); - nTickBoxed = length(iTickBox); - selectedVarNames = cell(1, nTickBoxed); - tickBoxes = get(varPanel, 'UserData'); - for i=1:nTickBoxed - selectedVarNames{i} = get(tickBoxes(iTickBox(i)), 'String'); - end - - % reset the varPanel - newVars = []; - createVarPanel(sam, []); - - % we want to be able to keep the selected variables from one dataset to another if possible - newTickBoxes = get(varPanel, 'UserData'); - for j=1:length(newTickBoxes) - varName = get(newTickBoxes(j), 'String'); - if any(strcmp(varName, selectedVarNames)) - newVars = [newVars j]; - end - end - - if ~isempty(newVars), createVarPanel(sam, newVars); end - selectionChange('set'); - end +% set window position +set(fig, 'Position', [0.1, 0.15, 0.8, 0.7]); - function graphMenuCallback(source,ev) - %GRAPHMENUCALLBACK Called when the user changes the graph type via the - % graph selection menu. Delegates to selectionChange. - % - selectionChange('graph'); - end +% restrict window to primary screen +set(fig, 'Units', 'pixels'); +pos = get(fig, 'OuterPosition'); +monitors = get(0, 'MonitorPositions'); +if pos(3) > monitors(1,3) + pos(1) = 1; + pos(3) = monitors(1,3); + set(fig, 'OuterPosition', pos); +end +set(fig, 'Units', 'normalized'); - function stateButtonCallback(source, ev) - %STATEBUTTONCALLBACK Called when the user pushes a state button. Updates - % the current state, then delegates to selectionChange. - % +% set widget positions +set(sidePanel, 'Position', posUi2(fig, 100, 100, 11:100, 1:10, 0)); +set(mainPanel, 'Position', posUi2(fig, 100, 100, 11:100, 11:100, 0)); +set(sampleMenu, 'Position', posUi2(fig, 100, 100, 1:5, 1:75, 0)); +set(graphMenu, 'Position', posUi2(fig, 100, 100, 1:5, 76:100, 0)); +set(extraSampleMenu, 'Position', posUi2(fig, 100, 100, 6:10, 1:75, 0)); +set(extraSampleCb, 'Position', posUi2(fig, 100, 100, 6:10, 76:100, 0)); - currentState = get(source, 'String'); - selectionChange('state'); - - % reset varPanel - sam = getSelectedData(); - vars = getSelectedVars(); - createVarPanel(sam, vars); - end +% varPanel and butPanel are positioned relative to sidePanel +set(butPanel, 'Position', posUi2(sidePanel, 10, 1, 1:5, 1, 0)); +set(varPanel, 'Position', posUi2(sidePanel, 10, 1, 6:10, 1, 0)); - function graphButtonCallback(source, ev) - %GRAPHBUTTONCALLBACK Called when the user pushes the 'Save Graph' button. - % Prompts the user to save the current graph. - - % find all axes objects on the main panel - ax = findobj(mainPanel, 'Type', 'axes'); - - % save the axes - saveGraph(fig, ax); - - end +% set state buttons position relative to butPanel +n = length(stateButtons); +for k = 1:n + set(stateButtons(k), 'Position', posUi2(butPanel, n, 1, k, 1, 0)); +end - function varPanelCallback(source,ev) - %PARAMPANELCALLBACK Called when the variable or dimension selection - % changes. Delegates to selectionChange. - % - selectionChange('var'); - end +% set callbacks - variable panel widget +% callbacks are set in createParamPanel +set(sampleMenu, 'Callback', @sampleMenuCallback); +set(graphMenu, 'Callback', @graphMenuCallback); +set(extraSampleCb, 'Callback', @sampleMenuCallback); +set(extraSampleMenu, 'Callback', @sampleMenuCallback); +set(stateButtons, 'Callback', @stateButtonCallback); - function zoomPostCallback(source,ev) - %ZOOMPOSTCALLBACK Called when the zoom function is called. Redraws axis ticks. - % - graphs1D = findobj('Tag', 'axis1D'); - graphs2D = findobj('Tag', 'axis2D'); - - isCurAx1D = false; - iGraph1D = (gca == graphs1D); - if any(iGraph1D) - isCurAx1D = true; - graphs1D(iGraph1D) = []; - else - graphs2D(gca == graphs2D) = []; - end - - graphs = [graphs1D; graphs2D]; - - % reset current axis yTicks - yLimits = get(gca, 'YLim'); - yStep = (yLimits(2) - yLimits(1)) / 5; - yTicks = yLimits(1):yStep:yLimits(2); - set(gca, 'YTick', yTicks); - - % sync all other 2D Y axis if needed - if ~isCurAx1D && ~isempty(graphs2D) - set(graphs2D, 'YLim', yLimits); - set(graphs2D, 'YTick', yTicks); - end - - % reset current axis xTicks - xLimits = get(gca, 'XLim'); - xStep = (xLimits(2) - xLimits(1)) / 5; - xTicks = xLimits(1):xStep:xLimits(2); - set(gca, 'XTick', xTicks); - - graphName = get(graphMenu, 'String'); - graphName = graphName{get(graphMenu, 'Value')}; - if strcmpi(graphName, 'TimeSeries') - % sync all other X axis - set(graphs, 'XLim', xLimits); - set(graphs, 'XTick', xTicks); - - % update other 1D axis yLim / yTick to reflect the - % change in the Y range of displayed data - if ~isempty(graphs1D) - sam = getSelectedData(); - hTickBoxes = get(varPanel, 'UserData'); - - qcSet = str2double(readProperty('toolbox.qc_set')); - rawFlag = imosQCFlag('raw', qcSet, 'flag'); - goodFlag = imosQCFlag('good', qcSet, 'flag'); - pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); - okFlags = [rawFlag goodFlag pGoodFlag]; +set(fig, 'Visible', 'on'); +createVarPanel(sample_data{1}, []); +selectionChange('state'); - for i=1:length(graphs1D) - userData = get(graphs1D(i), 'UserData'); - hData = userData{1}; - iTickBox = userData{2}; % index into currently ticked boxes - iAllTickedBox = find(cell2mat(get(hTickBoxes, 'Value')) == 1); - - varName = get(hTickBoxes(iAllTickedBox(iTickBox)), 'String'); - iVar = getVar(sam.variables, varName); - flags = sam.variables{iVar}.flags; - iGood = ismember(flags, okFlags); - - xData = get(hData, 'XData'); - yData = get(hData, 'YData'); - - xData(~iGood) = []; - yData(~iGood) = []; - - iDataIn = xData >= xLimits(1) & xData <= xLimits(2); - yLimits = [min(yData(iDataIn)), max(yData(iDataIn))]; - set(graphs1D(i), 'YLim', yLimits); - - yStep = (yLimits(2) - yLimits(1)) / 5; - yTicks = yLimits(1):yStep:yLimits(2); - - set(graphs1D(i), 'YTick', yTicks); - end - end - - % tranformation of datenum xticks in datestr - datetick(gca, 'x', 'dd-mm-yy HH:MM', 'keepticks'); - xTickLabel = get(gca, 'XTickLabel'); - for i=1:length(graphs) - set(graphs(i), 'XTickLabel', xTickLabel); % this is to avoid too many calls to datetick() - end - - elseif strcmpi(graphName, 'DepthProfile') - % sync all other Y axis - set(graphs, 'YLim', yLimits); - set(graphs, 'YTick', yTicks); - end - end +tb = findall(fig, 'Type', 'uitoolbar'); +buttons = findall(tb); - %% Menu callback - function displayCheckPressDiffs(source,ev, isQC) - %DISPLAYLINEPRESSDIFFS opens a new window where all the PRES/PRES_REL - %values for instruments adjacent to the current instrument are - %displayed with the differences between these instrument pressures - % - %check for pressure - iSampleMenu = get(sampleMenu, 'Value'); - iPRES_REL = getVar(sample_data{iSampleMenu}.variables, 'PRES_REL'); - iPRES = getVar(sample_data{iSampleMenu}.variables, 'PRES'); - if iPRES_REL == 0 && iPRES == 0 - sampleMenuStrings = get(sampleMenu, 'String'); - disp(['No pressure data for ' sampleMenuStrings{iSampleMenu}]) - return - end - - checkMooringPresDiffs(sample_data, iSampleMenu, isQC, false, ''); - end +savefigb = findobj(buttons, 'TooltipString', 'Save Figure'); +zoominb = findobj(buttons, 'TooltipString', 'Zoom In'); +zoomoutb = findobj(buttons, 'TooltipString', 'Zoom Out'); +panb = findobj(buttons, 'TooltipString', 'Pan'); +datacursorb = findobj(buttons, 'TooltipString', 'Data Cursor'); - function displayCheckPlannedDepths(source,ev, isQC) - %DISPLAYCHECKPLANNEDDEPTHS Opens a new window where the actual - %depths recorded are compared to the planned depths. - % - checkMooringPlannedDepths(sample_data, isQC, false, ''); - end - - function displayLineMooringDepth(source,ev, isQC) - %DISPLAYLINEMOORINGDEPTH Opens a new window where all the nominal depths and - %actual/computed depths from intruments on the mooring are line-plotted. - % - - lineMooring1DVar(sample_data, 'DEPTH', isQC, false, ''); +buttons(buttons == tb) = []; +buttons(buttons == savefigb) = []; +buttons(buttons == zoominb) = []; +buttons(buttons == zoomoutb) = []; +buttons(buttons == panb) = []; +buttons(buttons == datacursorb) = []; - end +delete(buttons); - function displayLineMooringVar(source,ev, isQC) - %DISPLAYLINEMOORINGVAR Opens a new window where all the previously selected - % variables collected by intruments on the mooring are line-plotted. - % - stringQC = 'non QC'; - if isQC, stringQC = 'QC'; end +% reset save figure callback with the FileSaveAs one instead of FileSave +set(savefigb, 'ClickedCallback', 'filemenufcn(gcbf,''FileSaveAs'')'); - lenSampleData = length(sample_data); - paramsName = {}; - paramsCount = []; - paramsName{1} = 'diff(TIME)'; - paramsCount(1) = lenSampleData; - % get all params that are in common in at least two datasets - for i=1:lenSampleData - lenParamsSample = length(sample_data{i}.variables); - for j=1:lenParamsSample - sameParam = strcmpi(paramsName, sample_data{i}.variables{j}.name); - if ~any(sameParam) - paramsName{end+1} = sample_data{i}.variables{j}.name; - paramsCount(end+1) = 1; - else - paramsCount(sameParam) = paramsCount(sameParam)+1; - end - end - end +%set zoom/pan post-callback +%zoom v6 off; % undocumented Matlab to make sure zoom function prior to R14 is not used. Seems to not be supported from R2015a. +hZoom = zoom(fig); +hPan = pan(fig); +set(hZoom, 'ActionPostCallback', @zoomPostCallback); +set(hPan, 'ActionPostCallback', @zoomPostCallback); + +% update zoom in and pan's tooltips to display hot keys +set(zoominb, 'TooltipString', 'Zoom In (Ctrl+z)'); +set(panb, 'TooltipString', 'Pan (Ctrl+a)'); - iParamsToGetRid = (paramsCount == 1); - paramsName(iParamsToGetRid) = []; +%set uimenu +hToolsMenu = uimenu(fig, 'label', 'Tools'); +switch mode + case 'timeSeries' + hToolsCheckPlannedDepths = uimenu(hToolsMenu, 'label', 'Check measured against planned depths'); + hToolsCheckPlannedDepthsNonQC = uimenu(hToolsCheckPlannedDepths, 'label', 'all data'); + hToolsCheckPlannedDepthsQC = uimenu(hToolsCheckPlannedDepths, 'label', 'only good and non QC''d data'); + hToolsCheckPressDiffs = uimenu(hToolsMenu, 'label', 'Check pressure differences between selected instrument and nearest neighbours'); + hToolsCheckPressDiffsNonQC = uimenu(hToolsCheckPressDiffs, 'label', 'all data'); + hToolsCheckPressDiffsQC = uimenu(hToolsCheckPressDiffs, 'label', 'only good and non QC''d data'); + hToolsLineDepth = uimenu(hToolsMenu, 'label', 'Line plot mooring''s depths'); + hToolsLineDepthNonQC = uimenu(hToolsLineDepth, 'label', 'all data'); + hToolsLineDepthQC = uimenu(hToolsLineDepth, 'label', 'only good and non QC''d data'); + hToolsLineCommonVar = uimenu(hToolsMenu, 'label', 'Line plot mooring''s 1D variables'); + hToolsLineCommonVarNonQC = uimenu(hToolsLineCommonVar, 'label', 'all data'); + hToolsLineCommonVarQC = uimenu(hToolsLineCommonVar, 'label', 'only good and non QC''d data'); + hToolsScatterCommonVar = uimenu(hToolsMenu, 'label', 'Scatter plot mooring''s 1D variables VS depth'); + hToolsScatterCommonVarNonQC = uimenu(hToolsScatterCommonVar, 'label', 'all data'); + hToolsScatterCommonVarQC = uimenu(hToolsScatterCommonVar, 'label', 'only good and non QC''d data'); + hToolsScatter2DCommonVar = uimenu(hToolsMenu, 'label', 'Scatter plot mooring''s 2D variables VS depth'); + hToolsScatter2DCommonVarNonQC = uimenu(hToolsScatter2DCommonVar, 'label', 'all data'); + hToolsScatter2DCommonVarQC = uimenu(hToolsScatter2DCommonVar, 'label', 'only good and non QC''d data'); + + %set menu callbacks + set(hToolsCheckPlannedDepthsNonQC, 'callBack', {@displayCheckPlannedDepths, false}); + set(hToolsCheckPlannedDepthsQC, 'callBack', {@displayCheckPlannedDepths, true}); + set(hToolsCheckPressDiffsNonQC, 'callBack', {@displayCheckPressDiffs, false}); + set(hToolsCheckPressDiffsQC, 'callBack', {@displayCheckPressDiffs, true}); + set(hToolsLineDepthNonQC, 'callBack', {@displayLineMooringDepth, false}); + set(hToolsLineDepthQC, 'callBack', {@displayLineMooringDepth, true}); + set(hToolsLineCommonVarNonQC, 'callBack', {@displayLineMooringVar, false}); + set(hToolsLineCommonVarQC, 'callBack', {@displayLineMooringVar, true}); + set(hToolsScatterCommonVarNonQC, 'callBack', {@displayScatterMooringVar, false, true}); + set(hToolsScatterCommonVarQC, 'callBack', {@displayScatterMooringVar, true, true}); + set(hToolsScatter2DCommonVarNonQC, 'callBack', {@displayScatterMooringVar, false, false}); + set(hToolsScatter2DCommonVarQC, 'callBack', {@displayScatterMooringVar, true, false}); + case 'profile' + hToolsLineCastVar = uimenu(hToolsMenu, 'label', 'Line plot profile variables'); + hToolsLineCastVarNonQC = uimenu(hToolsLineCastVar, 'label', 'all data'); + hToolsLineCastVarQC = uimenu(hToolsLineCastVar, 'label', 'only good and non QC''d data'); + + %set menu callbacks + set(hToolsLineCastVarNonQC, 'callBack', {@displayLineCastVar, false}); + set(hToolsLineCastVarQC, 'callBack', {@displayLineCastVar, true}); +end +hHotKeyMenu = uimenu(fig, 'label', 'Hot Keys'); +uimenu(hHotKeyMenu, 'Label', 'Enable zoom', 'Accelerator', 'z', 'Callback', @(src,evt)zoom(fig, 'on')); +uimenu(hHotKeyMenu, 'Label', 'Enable pan', 'Accelerator', 'a', 'Callback', @(src,evt)pan( fig, 'on')); +uimenu(hHotKeyMenu, 'Label', 'Disable zoom/pan', 'Accelerator', 'q', 'Callback', @disableZoomAndPan); - % we get rid of DEPTH parameter, if necessary user should use the - % Depth specific plot - iDEPTH = strcmpi(paramsName, 'DEPTH'); - paramsName(iDEPTH) = []; +hHelpMenu = uimenu(fig, 'label', 'Help'); +hHelpWiki = uimenu(hHelpMenu, 'label', 'IMOS Toolbox Wiki'); - % we get rid of TIMESERIES, PROFILE, TRAJECTORY, LATITUDE, LONGITUDE and NOMINAL_DEPTH parameters - iParam = strcmpi(paramsName, 'TIMESERIES'); - paramsName(iParam) = []; - iParam = strcmpi(paramsName, 'PROFILE'); - paramsName(iParam) = []; - iParam = strcmpi(paramsName, 'TRAJECTORY'); - paramsName(iParam) = []; - iParam = strcmpi(paramsName, 'LATITUDE'); - paramsName(iParam) = []; - iParam = strcmpi(paramsName, 'LONGITUDE'); - paramsName(iParam) = []; - iParam = strcmpi(paramsName, 'NOMINAL_DEPTH'); - paramsName(iParam) = []; - - % by default diff(TIME) is selected - iDiffTIME = find(strcmpi(paramsName, 'diff(TIME)')); +%set menu callbacks +set(hHelpWiki, 'callBack', @openWikiPage); - [iSelection, ok] = listdlg(... - 'ListString', paramsName, ... - 'SelectionMode', 'single', ... - 'ListSize', [150 150], ... - 'InitialValue', iDiffTIME, ... - 'Name', ['Plot a ' stringQC '''d variable accross all instruments in the mooring'], ... - 'PromptString', 'Select a variable :'); +%% Widget Callbacks - if ok==0 - return; - else - varName = paramsName{iSelection}; + function selectionChange(event) + %Retrieves the current selection, clears the main panel, and calls + % selectionCallback. + % + state = currentState; + sam = getSelectedData(); + vars = getSelectedVars(); + graph = getSelectedGraphType(); + + if get(extraSampleCb, 'value') + extraSam = getExtraSelectedData(); + else + extraSam.meta.index = 0; + end + + % clear main panel + children = get(mainPanel, 'Children'); + delete(children); + + % delete any mouse listeners that may have been added + zoom off; + pan off; + set(fig, 'WindowButtonDownFcn', []); + set(fig, 'WindowButtonMotionFcn', []); + set(fig, 'WindowButtonUpFcn', []); + + currentState = selectionCallback(... + event,... % string describing what triggered the callback. + mainPanel, ... % uipanel on which things can be drawn. + @updateCallback, ... % function to be called when data is modified. + state, ... % selected state (string). + sample_data, ... % cell array of sample_data structs + graph, ... % currently selected graph type (string). + sam.meta.index, ... % currently selected sample_data struct (index) + vars, ... % currently selected variables (indices). + extraSam.meta.index); % currently selected extra sample_data struct (index) + + % set data cursor mode custom display + dcm_obj = datacursormode(fig); + set(dcm_obj, 'UpdateFcn', {@customDcm, sam, vars, graph, mode}); + end + + function sampleMenuCallback(source, ev) + %SAMPLEMENUCALLBACK Called when a dataset is selected from the sample + % menu. Updates the variables panel, then delegates to selectionChange. + % + sam = getSelectedData(); + if get(extraSampleCb, 'value') + set(extraSampleMenu, 'Enable', 'on'); + else + set(extraSampleMenu, 'Enable', 'off'); + end + + % keep track of previously selected variables + iTickBox = getSelectedVars(); + nTickBoxed = length(iTickBox); + selectedVarNames = cell(1, nTickBoxed); + tickBoxes = get(varPanel, 'UserData'); + for i=1:nTickBoxed + selectedVarNames{i} = get(tickBoxes(iTickBox(i)), 'String'); + end + + % reset the varPanel + newVars = []; + createVarPanel(sam, []); + + % we want to be able to keep the selected variables from one dataset to another if possible + newTickBoxes = get(varPanel, 'UserData'); + for j=1:length(newTickBoxes) + varName = get(newTickBoxes(j), 'String'); + if any(strcmp(varName, selectedVarNames)) + newVars = [newVars j]; + end + end + + if ~isempty(newVars), createVarPanel(sam, newVars); end + selectionChange('set'); end - lineMooring1DVar(sample_data, varName, isQC, false, ''); + function graphMenuCallback(source, ev) + %GRAPHMENUCALLBACK Called when the user changes the graph type via the + % graph selection menu. Delegates to selectionChange. + % + selectionChange('graph'); + end - end + function stateButtonCallback(source, ev) + %STATEBUTTONCALLBACK Called when the user pushes a state button. Updates + % the current state, then delegates to selectionChange. + % + currentState = get(source, 'String'); + selectionChange('state'); + + % reset varPanel + sam = getSelectedData(); + vars = getSelectedVars(); + createVarPanel(sam, vars); + end -function disableZoomAndPan(source, ev) - pan(fig, 'off'); - zoom(fig, 'off'); -end + function varPanelCallback(source, ev) + %PARAMPANELCALLBACK Called when the variable or dimension selection + % changes. Delegates to selectionChange. + % + selectionChange('var'); + end -function displayLineCastVar(source,ev, isQC) - %DISPLAYLINECASTVAR Opens a new window where all the - % variables collected by the CTD cast are line-plotted. - % + function zoomPostCallback(source, ev) + %ZOOMPOSTCALLBACK Called when the zoom function is called. Redraws axis ticks. + % + graphs1D = findobj('Tag', 'axis1D'); + graphs2D = findobj('Tag', 'axis2D'); - % get all params names - lenSampleData = length(sample_data); - paramsName = {}; - for i=1:lenSampleData - lenParamsSample = length(sample_data{i}.variables); - for j=1:lenParamsSample - isParamQC = any(sample_data{i}.variables{j}.flags == 1) || any(sample_data{i}.variables{j}.flags == 2); - if i==1 && j==1 - paramsName{1} = sample_data{1}.variables{1}.name; - else - sameParam = strcmpi(paramsName, sample_data{i}.variables{j}.name); - if ~any(sameParam) - paramsName{end+1} = sample_data{i}.variables{j}.name; + isCurAx1D = false; + iGraph1D = (gca == graphs1D); + if any(iGraph1D) + isCurAx1D = true; + graphs1D(iGraph1D) = []; + else + graphs2D(gca == graphs2D) = []; + end + + graphs = [graphs1D; graphs2D]; + + % reset current axis yTicks + yLimits = get(gca, 'YLim'); + yStep = (yLimits(2) - yLimits(1)) / 5; + yTicks = yLimits(1):yStep:yLimits(2); + set(gca, 'YTick', yTicks); + + % sync all other 2D Y axis if needed + if ~isCurAx1D && ~isempty(graphs2D) + set(graphs2D, 'YLim', yLimits); + set(graphs2D, 'YTick', yTicks); + end + + % reset current axis xTicks + xLimits = get(gca, 'XLim'); + xStep = (xLimits(2) - xLimits(1)) / 5; + xTicks = xLimits(1):xStep:xLimits(2); + set(gca, 'XTick', xTicks); + + graphName = get(graphMenu, 'String'); + graphName = graphName{get(graphMenu, 'Value')}; + if strcmpi(graphName, 'TimeSeries') + % sync all other X axis + set(graphs, 'XLim', xLimits); + set(graphs, 'XTick', xTicks); + + % update other 1D axis yLim / yTick to reflect the + % change in the Y range of displayed data + if ~isempty(graphs1D) + sam = getSelectedData(); + extraSam = getExtraSelectedData(); + + hTickBoxes = get(varPanel, 'UserData'); + + qcSet = str2double(readProperty('toolbox.qc_set')); + rawFlag = imosQCFlag('raw', qcSet, 'flag'); + goodFlag = imosQCFlag('good', qcSet, 'flag'); + pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); + okFlags = [rawFlag goodFlag pGoodFlag]; + + for i=1:length(graphs1D) + userData = get(graphs1D(i), 'UserData'); + + hData = userData{1}; + xData = get(hData(1), 'XData'); + yData = get(hData(1), 'YData'); + + iTickBox = userData{2}; % index into currently ticked boxes + iAllTickedBox = find(cell2mat(get(hTickBoxes, 'Value')) == 1); + + varName = get(hTickBoxes(iAllTickedBox(iTickBox)), 'String'); + iVar = getVar(sam.variables, varName); + flags = sam.variables{iVar}.flags; + if get(extraSampleCb, 'value') + iExtraVar = getVar(extraSam.variables, varName); + if iExtraVar + flags = [flags; extraSam.variables{iExtraVar}.flags]; + xData = [xData, get(hData(2), 'XData')]; + yData = [yData, get(hData(2), 'YData')]; + end + end + iGood = ismember(flags, okFlags); + + xData(~iGood) = []; + yData(~iGood) = []; + + iDataIn = xData >= xLimits(1) & xData <= xLimits(2); + yLimits = [min(yData(iDataIn)), max(yData(iDataIn))]; + set(graphs1D(i), 'YLim', yLimits); + + yStep = (yLimits(2) - yLimits(1)) / 5; + yTicks = yLimits(1):yStep:yLimits(2); + + set(graphs1D(i), 'YTick', yTicks); end end - % get rid of non QC'd params if only interested in QC - if isQC && ~isParamQC; paramsName(end) = []; end + + % tranformation of datenum xticks in datestr + datetick(gca, 'x', 'dd-mm-yy HH:MM', 'keepticks'); + xTickLabel = get(gca, 'XTickLabel'); + for i=1:length(graphs) + set(graphs(i), 'XTickLabel', xTickLabel); % this is to avoid too many calls to datetick() + end + + elseif strcmpi(graphName, 'DepthProfile') + % sync all other Y axis + set(graphs, 'YLim', yLimits); + set(graphs, 'YTick', yTicks); end end - lineCastVar(sample_data, paramsName, isQC, false, ''); +%% Menu callback + function displayCheckPressDiffs(source, ev, isQC) + %DISPLAYLINEPRESSDIFFS opens a new window where all the PRES/PRES_REL + %values for instruments adjacent to the current instrument are + %displayed with the differences between these instrument pressures + % + %check for pressure + iSampleMenu = get(sampleMenu, 'Value'); + iPRES_REL = getVar(sample_data{iSampleMenu}.variables, 'PRES_REL'); + iPRES = getVar(sample_data{iSampleMenu}.variables, 'PRES'); + if iPRES_REL == 0 && iPRES == 0 + sampleMenuStrings = get(sampleMenu, 'String'); + disp(['No pressure data for ' sampleMenuStrings{iSampleMenu}]) + return + end + + checkMooringPresDiffs(sample_data, iSampleMenu, isQC, false, ''); + end - end + function displayCheckPlannedDepths(source, ev, isQC) + %DISPLAYCHECKPLANNEDDEPTHS Opens a new window where the actual + %depths recorded are compared to the planned depths. + % + checkMooringPlannedDepths(sample_data, isQC, false, ''); + end -function displayScatterMooringVar(source,ev, isQC, is1D) - %DISPLAYSCATTERMOORINGVAR Opens a new window where all the previously selected - % variables collected by intruments on the mooring are scatter-plotted. - % - stringQC = 'non QC'; - if isQC, stringQC = 'QC'; end + function displayLineMooringDepth(source, ev, isQC) + %DISPLAYLINEMOORINGDEPTH Opens a new window where all the nominal depths and + %actual/computed depths from intruments on the mooring are line-plotted. + % + lineMooring1DVar(sample_data, 'DEPTH', isQC, false, ''); + + end - % go through all datasets and parameters and count them - lenSampleData = length(sample_data); - paramsName = {}; - paramsCount = []; - params2D = []; - for i=1:lenSampleData - lenParamsSample = length(sample_data{i}.variables); - for j=1:lenParamsSample - sameParam = strcmpi(paramsName, sample_data{i}.variables{j}.name); - if ~any(sameParam) - paramsName{end+1} = sample_data{i}.variables{j}.name; - paramsCount(end+1) = 1; - if length(sample_data{i}.variables{j}.dimensions) == 2 && ... % TIME, HEIGHT_ABOVE_SENSOR - size(sample_data{i}.variables{j}.data, 2) > 1 && ... - size(sample_data{i}.variables{j}.data, 3) == 1 % we're only plotting ADCP 2D variables with DEPTH variable. - params2D(end+1) = true; + function displayLineMooringVar(source, ev, isQC) + %DISPLAYLINEMOORINGVAR Opens a new window where all the previously selected + % variables collected by intruments on the mooring are line-plotted. + % + stringQC = 'non QC'; + if isQC, stringQC = 'QC'; end + + lenSampleData = length(sample_data); + paramsName = {}; + paramsCount = []; + paramsName{1} = 'diff(TIME)'; + paramsCount(1) = lenSampleData; + % get all params that are in common in at least two datasets + for i=1:lenSampleData + lenParamsSample = length(sample_data{i}.variables); + for j=1:lenParamsSample + sameParam = strcmpi(paramsName, sample_data{i}.variables{j}.name); + if ~any(sameParam) + paramsName{end+1} = sample_data{i}.variables{j}.name; + paramsCount(end+1) = 1; else - params2D(end+1) = false; + paramsCount(sameParam) = paramsCount(sameParam)+1; end - else - paramsCount(sameParam) = paramsCount(sameParam)+1; end end - end - - if is1D - % get only params that are in common in at least two datasets - iParamsNotInCommon = (paramsCount == 1); - % get only params that are 1D - iParamsToGetRid = params2D | iParamsNotInCommon; + iParamsToGetRid = (paramsCount == 1); paramsName(iParamsToGetRid) = []; - % we get rid of DEPTH, PRES and PRES_REL parameters - iDEPTH = strcmpi('DEPTH', paramsName); - paramsName(iDEPTH) = []; - iDEPTH = strcmpi('PRES', paramsName); + % we get rid of DEPTH parameter, if necessary user should use the + % Depth specific plot + iDEPTH = strcmpi(paramsName, 'DEPTH'); paramsName(iDEPTH) = []; - iDEPTH = strcmpi('PRES_REL', paramsName); - paramsName(iDEPTH) = []; - + % we get rid of TIMESERIES, PROFILE, TRAJECTORY, LATITUDE, LONGITUDE and NOMINAL_DEPTH parameters iParam = strcmpi(paramsName, 'TIMESERIES'); paramsName(iParam) = []; @@ -737,209 +622,323 @@ function displayScatterMooringVar(source,ev, isQC, is1D) iParam = strcmpi(paramsName, 'NOMINAL_DEPTH'); paramsName(iParam) = []; - % by default TEMP is selected - iDefault = find(strcmpi(paramsName, 'TEMP')); - else - % get only params that are 2D - paramsName(~params2D) = []; + % by default diff(TIME) is selected + iDiffTIME = find(strcmpi(paramsName, 'diff(TIME)')); - % we get rid of DEPTH, PRES and PRES_REL parameters - iDEPTH = strcmpi('DEPTH', paramsName); - paramsName(iDEPTH) = []; - iDEPTH = strcmpi('PRES', paramsName); - paramsName(iDEPTH) = []; - iDEPTH = strcmpi('PRES_REL', paramsName); - paramsName(iDEPTH) = []; - - % we get rid of HEIGHT parameter - iHEIGHT = strcmpi('HEIGHT', paramsName); - paramsName(iHEIGHT) = []; + [iSelection, ok] = listdlg(... + 'ListString', paramsName, ... + 'SelectionMode', 'single', ... + 'ListSize', [150 150], ... + 'InitialValue', iDiffTIME, ... + 'Name', ['Plot a ' stringQC '''d variable accross all instruments in the mooring'], ... + 'PromptString', 'Select a variable :'); - % we get rid of ADCP diagnostic parameters - for i=1:4 - iStr = num2str(i); - iABSI = strcmpi(['ABSI' iStr], paramsName); - paramsName(iABSI) = []; - iABSIC = strcmpi(['ABSIC' iStr], paramsName); - paramsName(iABSIC) = []; - iCORR = strcmpi(['CMAG' iStr], paramsName); - paramsName(iCORR) = []; - iPERG = strcmpi(['PERG' iStr], paramsName); - paramsName(iPERG) = []; + if ok==0 + return; + else + varName = paramsName{iSelection}; end - - % by default CDIR is selected - iDefault = find(strcmpi(paramsName, 'CDIR')); + + lineMooring1DVar(sample_data, varName, isQC, false, ''); + end - if isempty(iDefault), iDefault = 1; end - - [iSelection, ok] = listdlg(... - 'ListString', paramsName, ... - 'SelectionMode', 'single', ... - 'ListSize', [150 150], ... - 'InitialValue', iDefault, ... - 'Name', ['Plot a ' stringQC '''d variable accross all instruments in the mooring'], ... - 'PromptString', 'Select a variable :'); - - if ok==0 - return; - else - varName = paramsName{iSelection}; + function disableZoomAndPan(source, ev) + pan( fig, 'off'); + zoom(fig, 'off'); end - if is1D - scatterMooring1DVarAgainstDepth(sample_data, varName, isQC, false, ''); - else - scatterMooring2DVarAgainstDepth(sample_data, varName, isQC, false, ''); + function displayLineCastVar(source, ev, isQC) + %DISPLAYLINECASTVAR Opens a new window where all the + % variables collected by the CTD cast are line-plotted. + % + + % get all params names + lenSampleData = length(sample_data); + paramsName = {}; + for i=1:lenSampleData + lenParamsSample = length(sample_data{i}.variables); + for j=1:lenParamsSample + isParamQC = any(sample_data{i}.variables{j}.flags == 1) || any(sample_data{i}.variables{j}.flags == 2); + if i==1 && j==1 + paramsName{1} = sample_data{1}.variables{1}.name; + else + sameParam = strcmpi(paramsName, sample_data{i}.variables{j}.name); + if ~any(sameParam) + paramsName{end+1} = sample_data{i}.variables{j}.name; + end + end + % get rid of non QC'd params if only interested in QC + if isQC && ~isParamQC; paramsName(end) = []; end + end + end + lineCastVar(sample_data, paramsName, isQC, false, ''); end -end + function displayScatterMooringVar(source, ev, isQC, is1D) + %DISPLAYSCATTERMOORINGVAR Opens a new window where all the previously selected + % variables collected by intruments on the mooring are scatter-plotted. + % + stringQC = 'non QC'; + if isQC, stringQC = 'QC'; end + + % go through all datasets and parameters and count them + lenSampleData = length(sample_data); + paramsName = {}; + paramsCount = []; + params2D = []; + for i=1:lenSampleData + lenParamsSample = length(sample_data{i}.variables); + for j=1:lenParamsSample + sameParam = strcmpi(paramsName, sample_data{i}.variables{j}.name); + if ~any(sameParam) + paramsName{end+1} = sample_data{i}.variables{j}.name; + paramsCount(end+1) = 1; + if length(sample_data{i}.variables{j}.dimensions) == 2 && ... % TIME, HEIGHT_ABOVE_SENSOR + size(sample_data{i}.variables{j}.data, 2) > 1 && ... + size(sample_data{i}.variables{j}.data, 3) == 1 % we're only plotting ADCP 2D variables with DEPTH variable. + params2D(end+1) = true; + else + params2D(end+1) = false; + end + else + paramsCount(sameParam) = paramsCount(sameParam)+1; + end + end + end + + if is1D + % get only params that are in common in at least two datasets + iParamsNotInCommon = (paramsCount == 1); + % get only params that are 1D + iParamsToGetRid = params2D | iParamsNotInCommon; + + paramsName(iParamsToGetRid) = []; + + % we get rid of DEPTH, PRES and PRES_REL parameters + iDEPTH = strcmpi('DEPTH', paramsName); + paramsName(iDEPTH) = []; + iDEPTH = strcmpi('PRES', paramsName); + paramsName(iDEPTH) = []; + iDEPTH = strcmpi('PRES_REL', paramsName); + paramsName(iDEPTH) = []; + + % we get rid of TIMESERIES, PROFILE, TRAJECTORY, LATITUDE, LONGITUDE and NOMINAL_DEPTH parameters + iParam = strcmpi(paramsName, 'TIMESERIES'); + paramsName(iParam) = []; + iParam = strcmpi(paramsName, 'PROFILE'); + paramsName(iParam) = []; + iParam = strcmpi(paramsName, 'TRAJECTORY'); + paramsName(iParam) = []; + iParam = strcmpi(paramsName, 'LATITUDE'); + paramsName(iParam) = []; + iParam = strcmpi(paramsName, 'LONGITUDE'); + paramsName(iParam) = []; + iParam = strcmpi(paramsName, 'NOMINAL_DEPTH'); + paramsName(iParam) = []; + + % by default TEMP is selected + iDefault = find(strcmpi(paramsName, 'TEMP')); + else + % get only params that are 2D + paramsName(~params2D) = []; + + % we get rid of DEPTH, PRES and PRES_REL parameters + iDEPTH = strcmpi('DEPTH', paramsName); + paramsName(iDEPTH) = []; + iDEPTH = strcmpi('PRES', paramsName); + paramsName(iDEPTH) = []; + iDEPTH = strcmpi('PRES_REL', paramsName); + paramsName(iDEPTH) = []; + + % we get rid of HEIGHT parameter + iHEIGHT = strcmpi('HEIGHT', paramsName); + paramsName(iHEIGHT) = []; + + % we get rid of ADCP diagnostic parameters + for i=1:4 + iStr = num2str(i); + iABSI = strcmpi(['ABSI' iStr], paramsName); + paramsName(iABSI) = []; + iABSIC = strcmpi(['ABSIC' iStr], paramsName); + paramsName(iABSIC) = []; + iCORR = strcmpi(['CMAG' iStr], paramsName); + paramsName(iCORR) = []; + iPERG = strcmpi(['PERG' iStr], paramsName); + paramsName(iPERG) = []; + end + + % by default CDIR is selected + iDefault = find(strcmpi(paramsName, 'CDIR')); + end + + if isempty(iDefault), iDefault = 1; end + + [iSelection, ok] = listdlg(... + 'ListString', paramsName, ... + 'SelectionMode', 'single', ... + 'ListSize', [150 150], ... + 'InitialValue', iDefault, ... + 'Name', ['Plot a ' stringQC '''d variable accross all instruments in the mooring'], ... + 'PromptString', 'Select a variable :'); + + if ok==0 + return; + else + varName = paramsName{iSelection}; + end + + if is1D + scatterMooring1DVarAgainstDepth(sample_data, varName, isQC, false, ''); + else + scatterMooring2DVarAgainstDepth(sample_data, varName, isQC, false, ''); + end + + end - function openWikiPage(source,ev) - %OPENWIKIPAGE opens a new tab in your web-browser to access the - %IMOS-Toolbox wiki - % - url = 'https://github.com/aodn/imos-toolbox/wiki'; - stat = web(url, '-browser'); - if stat == 1 - fprintf('%s\n', 'Warning : Browser was not found.'); - elseif stat == 2 - fprintf('%s\n', 'Warning : Browser was found but could not be launched.'); + function openWikiPage(source, ev) + %OPENWIKIPAGE opens a new tab in your web-browser to access the + %IMOS-Toolbox wiki + % + url = 'https://github.com/aodn/imos-toolbox/wiki'; + stat = web(url, '-browser'); + if stat == 1 + fprintf('%s\n', 'Warning : Browser was not found.'); + elseif stat == 2 + fprintf('%s\n', 'Warning : Browser was found but could not be launched.'); + end end - end - %% Data update callback - - function updateCallback(sam) - %UPDATECALLBACK Called when a data set has been modified. Saves the new - % copy of the data set. - % - narginchk(1,1); - if ~isstruct(sam), error('sam must be a struct'); end - if ~isfield(sam.meta, 'index'), error('sam must have an index field'); end - - % synchronise current data sets and sample_data - sample_data{sam.meta.index} = sam; - lenSam = length(sample_data); - - % regenerate descriptions - sampleDataDescs = cell(lenSam, 1); - for k = 1:lenSam - sampleDataDescs{k} = genSampleDataDesc(sample_data{k}); +%% Data update callback + + function updateCallback(sam) + %UPDATECALLBACK Called when a data set has been modified. Saves the new + % copy of the data set. + % + narginchk(1,1); + if ~isstruct(sam), error('sam must be a struct'); end + if ~isfield(sam.meta, 'index'), error('sam must have an index field'); end + + % synchronise current data sets and sample_data + sample_data{sam.meta.index} = sam; + lenSam = length(sample_data); + + % regenerate descriptions + sampleDataDescs = cell(lenSam, 1); + for i = 1:lenSam + sampleDataDescs{i} = genSampleDataDesc(sample_data{i}); + end + + set(sampleMenu, 'String', sampleDataDescs); + set(extraSampleMenu, 'String', sampleDataDescs); + end - - set(sampleMenu, 'String', sampleDataDescs); - - end - %% Retrieving current selection +%% Retrieving current selection + + function sam = getSelectedData() + %GETSELECTEDDATA Returns the currently selected sample_data. + % + idx = get(sampleMenu, 'Value'); + + sam = sample_data{idx}; + end - function sam = getSelectedData() - %GETSELECTEDDATA Returns the currently selected sample_data. - % - idx = get(sampleMenu, 'Value'); - - sam = sample_data{idx}; - end + function sam = getExtraSelectedData() + %GETEXTRASELECTEDDATA Returns the currently selected extra sample_data. + % + idx = get(extraSampleMenu, 'Value'); + + sam = sample_data{idx}; + end - function graphType = getSelectedGraphType() - %GETSELECTEDGRAPHTYPE Returns the currently selected graph type. - % - idx = get(graphMenu, 'Value'); - types = get(graphMenu, 'String'); - - graphType = types{idx}; - - end + function graphType = getSelectedGraphType() + %GETSELECTEDGRAPHTYPE Returns the currently selected graph type. + % + idx = get(graphMenu, 'Value'); + types = get(graphMenu, 'String'); + + graphType = types{idx}; + + end - function vars = getSelectedVars() - %GETSELECTEDVARS Returns a vector containing the indices of the - % variables which are selected. - % - - % menu and checkboxes are stored in user data - checkboxes = get(varPanel, 'UserData'); - - vars = []; - - for m = 1:length(checkboxes) - if get(checkboxes(m), 'Value'), vars(end+1) = m; end + function vars = getSelectedVars() + %GETSELECTEDVARS Returns a vector containing the indices of the + % variables which are selected. + % + + % menu and checkboxes are stored in user data + checkboxes = get(varPanel, 'UserData'); + + vars = []; + + for m = 1:length(checkboxes) + if get(checkboxes(m), 'Value'), vars(end+1) = m; end + end end - end - %% Miscellaneous - - function createVarPanel(sam, vars) - %CREATEVARPANEL Creates the variable selection panel. Called when the - % selected dataset changes. The panel allows users to select which - % variables should be displayed. - % - % delete checkboxes and dim menu from previous data set - checkboxes = get(varPanel, 'Children'); - for m = 1:length(checkboxes), delete(checkboxes(m)); end - - switch mode - case 'profile' - % we don't want to plot TIME, PROFILE, DIRECTION, LATITUDE, LONGITUDE, BOT_DEPTH - p = getVar(sam.variables, 'BOT_DEPTH'); +%% Miscellaneous + + function createVarPanel(sam, vars) + %CREATEVARPANEL Creates the variable selection panel. Called when the + % selected dataset changes. The panel allows users to select which + % variables should be displayed. + % + % delete checkboxes and dim menu from previous data set + checkboxes = get(varPanel, 'Children'); + for m = 1:length(checkboxes), delete(checkboxes(m)); end + + switch mode + case 'profile' + % we don't want to plot TIME, PROFILE, DIRECTION, LATITUDE, LONGITUDE, BOT_DEPTH + p = getVar(sam.variables, 'BOT_DEPTH'); - % we don't want to plot DEPTH if it's a variable - iDepth = getVar(sam.variables, 'DEPTH'); - if iDepth ~= 0 - sam.variables(iDepth) = []; - end - case 'timeSeries' - % we don't want to plot TIMESERIES, PROFILE, TRAJECTORY, LATITUDE, LONGITUDE, NOMINAL_DEPTH - p = getVar(sam.variables, 'NOMINAL_DEPTH'); - end + % we don't want to plot DEPTH if it's a variable + iDepth = getVar(sam.variables, 'DEPTH'); + if iDepth ~= 0 + sam.variables(iDepth) = []; + end + case 'timeSeries' + % we don't want to plot TIMESERIES, PROFILE, TRAJECTORY, LATITUDE, LONGITUDE, NOMINAL_DEPTH + p = getVar(sam.variables, 'NOMINAL_DEPTH'); + end - % create checkboxes for new data set. The order in which the checkboxes - % are created is the same as the order of the variables - this is - % important, as the getSelectedParams function assumes that the indices - % line up. - checkboxes(:) = []; - n = length(sam.variables); - for m = 1+p:n - if isempty(vars) - % enable at most 3 variables initially, - % otherwise the graph will be cluttered - val = 1; - if m-p > 3, val = 0; end - else - if any(m-p == vars) + % create checkboxes for new data set. The order in which the checkboxes + % are created is the same as the order of the variables - this is + % important, as the getSelectedParams function assumes that the indices + % line up. + checkboxes(:) = []; + n = length(sam.variables); + for m = 1+p:n + if isempty(vars) + % enable at most 3 variables initially, + % otherwise the graph will be cluttered val = 1; + if m-p > 3, val = 0; end else - val = 0; + if any(m-p == vars) + val = 1; + else + val = 0; + end end + + checkboxes(m-p) = uicontrol(... + 'Parent', varPanel,... + 'Style', 'checkbox',... + 'String', sam.variables{m}.name,... + 'TooltipString', sprintf('%s\n(%s)', sam.variables{m}.long_name, sam.variables{m}.units),... + 'Value', val,... + 'Callback', @varPanelCallback,... + 'Units', 'normalized',... + 'Position', posUi2(varPanel, n-p, 1, m-p, 1, 0),... + 'Tag', ['checkbox' sam.variables{m}.name]); end - -% checkboxes(m) = uicontrol(... -% 'Parent', varPanel,... -% 'Style', 'checkbox',... -% 'String', sam.variables{m}.name,... -% 'Value', val,... -% 'Callback', @varPanelCallback,... -% 'Units', 'normalized',... -% 'Position', [0.0, (n-m)/n, 1.0, 1/n]); - checkboxes(m-p) = uicontrol(... - 'Parent', varPanel,... - 'Style', 'checkbox',... - 'String', sam.variables{m}.name,... - 'TooltipString', sprintf('%s\n(%s)', sam.variables{m}.long_name, sam.variables{m}.units),... - 'Value', val,... - 'Callback', @varPanelCallback,... - 'Units', 'normalized',... - 'Position', posUi2(varPanel, n-p, 1, m-p, 1, 0),... - 'Tag', ['checkbox' sam.variables{m}.name]); + % the checkboxes are saved in UserData field to make them + % easy to retrieve in the getSelectedVars function + set(varPanel, 'UserData', checkboxes); end -% set(checkboxes, 'Units', 'pixels'); - - % the checkboxes are saved in UserData field to make them - % easy to retrieve in the getSelectedVars function - set(varPanel, 'UserData', checkboxes); - end function txt = customDcm(~, event_obj, sam, vars, graph, mode) % Customizes text of data tips @@ -947,21 +946,20 @@ function createVarPanel(sam, vars) case 'profile' % we don't want to plot TIME, PROFILE, DIRECTION, LATITUDE, LONGITUDE, BOT_DEPTH varOffset = getVar(sam.variables, 'BOT_DEPTH'); - dimLabel = 'DEPTH'; - dimUnit = ' m'; - dimFun = @num2str; + dimLabel = 'DEPTH'; + dimUnit = ' m'; + dimFun = @num2str; case 'timeSeries' % we don't want to plot TIMESERIES, PROFILE, TRAJECTORY, LATITUDE, LONGITUDE, NOMINAL_DEPTH varOffset = getVar(sam.variables, 'NOMINAL_DEPTH'); - dimLabel = 'TIME'; - dimUnit = ' UTC'; - dimFun = @datestr; + dimLabel = 'TIME'; + dimUnit = ' UTC'; + dimFun = @datestr; end - % retrieve x/y click positions + data index + % retrieve x/y click positions posClic = get(event_obj, 'Position'); - I = get(event_obj, 'DataIndex'); iDim = getVar(sam.dimensions, dimLabel); nRecord = length(sam.dimensions{iDim}.data); diff --git a/Graph/DepthProfile/graphDepthProfileGeneric.m b/Graph/DepthProfile/graphDepthProfileGeneric.m index 616cbba5b..e84189e76 100644 --- a/Graph/DepthProfile/graphDepthProfileGeneric.m +++ b/Graph/DepthProfile/graphDepthProfileGeneric.m @@ -1,4 +1,4 @@ -function [h, labels] = graphDepthProfileGeneric( ax, sample_data, var ) +function [h, labels] = graphDepthProfileGeneric( ax, sample_data, var, color ) %GRAPHDEPTHPROFILEGENERIC Plots the given variable (x axis) against depth % (y axis). % @@ -6,6 +6,7 @@ % ax - Parent axis. % sample_data - The data set. % var - The variable to plot. +% color - The color to be used to plot the variable. % % Outputs: % h - Handle(s) to the line(s) which was/were plotted. @@ -44,7 +45,7 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % -narginchk(3,3); +narginchk(4,4); if ~ishandle(ax), error('ax must be a graphics handle'); end if ~isstruct(sample_data), error('sample_data must be a struct'); end @@ -71,9 +72,9 @@ switch mode case 'profile' - h = line(var.data(:, 1), depth.data(:, 1), 'Parent', ax, 'LineStyle', '-'); % downcast + h = line(var.data(:, 1), depth.data(:, 1), 'Parent', ax, 'LineStyle', '-', 'Color', color); % downcast if size(var.data, 2) > 1 - h(end+1) = line(var.data(:, 2), depth.data(:, 2), 'Parent', ax, 'LineStyle', '--'); % upcast + h(end+1) = line(var.data(:, 2), depth.data(:, 2), 'Parent', ax, 'LineStyle', '--', 'Color', color); % upcast end case 'timeSeries' @@ -81,10 +82,10 @@ % ADCP data, we look for vertical dimension iVertDim = var.dimensions(2); for i=1:size(var.data, 2) - h(i) = line(var.data(:, i), depth.data - sample_data.dimensions{iVertDim}.data(i), 'Parent', ax, 'LineStyle', '-'); + h(i) = line(var.data(:, i), depth.data - sample_data.dimensions{iVertDim}.data(i), 'Parent', ax, 'LineStyle', '-', 'Color', color); end else - h = line(var.data, depth.data, 'Parent', ax, 'LineStyle', '-'); + h = line(var.data, depth.data, 'Parent', ax, 'LineStyle', '-', 'Color', color); end end @@ -105,6 +106,9 @@ end end +% set background to be grey +set(ax, 'Color', [0.85 0.85 0.85]) + labels = {var.name, 'DEPTH'}; % assume that the depth data is ascending - we want to display diff --git a/Graph/TimeSeries/graphTimeSeriesGeneric.m b/Graph/TimeSeries/graphTimeSeriesGeneric.m index b4dcc7b52..5de125534 100644 --- a/Graph/TimeSeries/graphTimeSeriesGeneric.m +++ b/Graph/TimeSeries/graphTimeSeriesGeneric.m @@ -97,7 +97,7 @@ end % set background to be grey -set(ax, 'Color', [0.75 0.75 0.75]) +set(ax, 'Color', [0.85 0.85 0.85]) if strncmp(var.name, 'DEPTH', 4) || strncmp(var.name, 'PRES', 4) || strncmp(var.name, 'PRES_REL', 8) set(ax, 'YDir', 'reverse'); diff --git a/Graph/TimeSeries/highlightTimeSeriesGeneric.m b/Graph/TimeSeries/highlightTimeSeriesGeneric.m index 570eeb76a..7de7e5b6c 100644 --- a/Graph/TimeSeries/highlightTimeSeriesGeneric.m +++ b/Graph/TimeSeries/highlightTimeSeriesGeneric.m @@ -59,8 +59,8 @@ if ~isstruct(variable), error('variable must be a struct'); end if ~ischar(type), error('type must be a string'); end -xdata = get(data, 'XData'); -ydata = get(data, 'YData'); +xdata = get(data(1), 'XData'); % data(1) retrieves the first graphic handle only in case extra sample is selected +ydata = get(data(1), 'YData'); if iscell(xdata) xdata = cell2mat(xdata)'; diff --git a/Graph/Transect/graphTransectGeneric.m b/Graph/Transect/graphTransectGeneric.m index 18d2333ae..f0e9ee93a 100644 --- a/Graph/Transect/graphTransectGeneric.m +++ b/Graph/Transect/graphTransectGeneric.m @@ -1,4 +1,4 @@ -function [h labels] = graphTransectGeneric( ax, sample_data, var ) +function [h, labels] = graphTransectGeneric( ax, sample_data, var ) %GRAPHTRANSECTGENERIC Plots the given variable as 2d transect data. Assumes % that the sample data struct contains latitude and longitude variable data. % @@ -72,4 +72,7 @@ if length(cbLabel) > 20, cbLabel = [cbLabel(1:17) '...']; end set(get(cb, 'YLabel'), 'String', cbLabel, 'Interpreter', 'none'); +% set background to be grey +set(ax, 'Color', [0.85 0.85 0.85]) + labels = {'LATITUDE', 'LONGITUDE'}; diff --git a/Graph/XvY/graphXvYGeneric.m b/Graph/XvY/graphXvYGeneric.m index 74d9ef1ae..be99daba2 100644 --- a/Graph/XvY/graphXvYGeneric.m +++ b/Graph/XvY/graphXvYGeneric.m @@ -1,4 +1,4 @@ -function [h, labels] = graphXvYGeneric( ax, sample_data, vars ) +function [h, labels] = graphXvYGeneric( ax, sample_data, vars, color ) %GRAPHXVYGENERIC Plots the given variable (x axis) against another % (y axis). % @@ -6,6 +6,7 @@ % ax - Parent axis. % sample_data - The data set. % vars - The variables to plot. +% color - The color to be used to plot the variable. % % Outputs: % h - Handle(s) to the line(s) which was/were plotted. @@ -44,7 +45,7 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % -narginchk(3,3); +narginchk(4,4); if ~ishandle(ax), error('ax must be a graphics handle'); end if ~isstruct(sample_data), error('sample_data must be a struct'); end @@ -53,7 +54,7 @@ xdata = sample_data.variables{vars(1)}.data; ydata = sample_data.variables{vars(2)}.data; -h = line(xdata, ydata); +h = line(xdata, ydata, 'Color', color); set(ax, 'Tag', 'axis1D'); % for global/regional range display @@ -77,4 +78,7 @@ end end +% set background to be grey +set(ax, 'Color', [0.85 0.85 0.85]) + labels = {sample_data.variables{vars(1)}.name, sample_data.variables{vars(2)}.name}; \ No newline at end of file diff --git a/Graph/checkMooringPlannedDepths.m b/Graph/checkMooringPlannedDepths.m index c807b4cbd..cb250e5aa 100644 --- a/Graph/checkMooringPlannedDepths.m +++ b/Graph/checkMooringPlannedDepths.m @@ -84,7 +84,7 @@ function checkMooringPlannedDepths(sample_data, isQC, saveToFile, exportDir) timeVar = dataVar; isPlottable = false; -backgroundColor = [0.75 0.75 0.75]; +backgroundColor = [0.85 0.85 0.85]; for i=1:lenSampleData %only look at instruments with pressure diff --git a/Graph/checkMooringPresDiffs.m b/Graph/checkMooringPresDiffs.m index 432adbb2b..46842e219 100644 --- a/Graph/checkMooringPresDiffs.m +++ b/Graph/checkMooringPresDiffs.m @@ -137,7 +137,7 @@ function checkMooringPresDiffs(sample_data, iSampleMenu, isQC, saveToFile, expor isPlottable = false; -backgroundColor = [0.75 0.75 0.75]; +backgroundColor = [0.85 0.85 0.85]; %plot fileName = genIMOSFileName(sample_data{iCurrSam}, 'png'); diff --git a/Graph/graphDepthProfile.m b/Graph/graphDepthProfile.m index a13070e82..d138162cb 100644 --- a/Graph/graphDepthProfile.m +++ b/Graph/graphDepthProfile.m @@ -1,4 +1,4 @@ -function [graphs, lines, vars] = graphDepthProfile( parent, sample_data, vars ) +function [graphs, lines, vars] = graphDepthProfile( parent, sample_data, vars, extra_sample_data ) %GRAPHDEPTHPROFILE Graphs the given data in a depth profile style using % subplots. % @@ -10,6 +10,7 @@ % parent - handle to the parent container. % sample_data - struct containing sample data. % vars - Indices of variables that should be graphed. +% extra_sample_data - struct containing extra sample data. % % Outputs: % graphs - A vector of handles to axes on which the data has @@ -52,11 +53,13 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(3,3); + narginchk(4,4); - if ~ishandle( parent), error('parent must be a handle'); end - if ~isstruct( sample_data), error('sample_data must be a struct'); end - if ~isnumeric(vars), error('vars must be a numeric'); end + if ~ishandle( parent), error('parent must be a handle'); end + if ~isstruct( sample_data), error('sample_data must be a struct'); end + if ~isnumeric(vars), error('vars must be a numeric'); end + if ~isstruct(extra_sample_data) && ... + ~isempty(extra_sample_data), error('extra_sample_data must be a struct or empty'); end graphs = []; lines = []; @@ -114,6 +117,18 @@ name = sample_data.variables{vars(k)}.name; + iExtraVar = 0; + if ~isempty(extra_sample_data) + iExtraVar = getVar(extra_sample_data.variables, name); + iExtraDepth = getVar(extra_sample_data.variables, 'DEPTH'); + if iExtraDepth == 0 + iExtraDepth = getVar(extra_sample_data.dimensions, 'DEPTH'); + extraDepth = extra_sample_data.dimensions{iExtraDepth}.data; + else + extraDepth = extra_sample_data.variables{iExtraDepth}.data; + end + end + % create the axes; the subplots are laid out horizontally graphs(k) = subplot(1, length(vars), k); @@ -122,20 +137,24 @@ 'Color', 'none',... 'YGrid', 'on'); - % make sure line colour alternate; because we are creating - % multiple axes, this is not done automatically for us - col = get(graphs(k), 'ColorOrder'); - col = col(mod(k, length(col))+1, :); - % plot the variable plotFunc = getGraphFunc('DepthProfile', 'graph', name); - [lines(k,:), labels] = plotFunc(graphs(k), sample_data, vars(k)); - - % set the line colour - wrap in a try block, - % as surface plot colour cannot be set - try set(lines(k,:), 'Color', col); - catch e + if iExtraVar + if extra_sample_data.meta.level == 1 + qcSet = str2double(readProperty('toolbox.qc_set')); + goodFlag = imosQCFlag('good', qcSet, 'flag'); + pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); + rawFlag = imosQCFlag('raw', qcSet, 'flag'); + + iGoodExtra = (extra_sample_data.variables{iExtraVar}.flags == goodFlag) | ... + (extra_sample_data.variables{iExtraVar}.flags == pGoodFlag) | ... + (extra_sample_data.variables{iExtraVar}.flags == rawFlag); + extra_sample_data.variables{iExtraVar}.data(~iGoodExtra) = NaN; + end + plotFunc(graphs(k), extra_sample_data, iExtraVar, 'k'); % extra instrument is always plotted in black end + % we plot the current instrument last so that it appears on top + [lines(k,:), labels] = plotFunc(graphs(k), sample_data, vars(k), 'b'); % current instrument is always plotted in blue % set x label uom = ''; @@ -155,6 +174,30 @@ set(get(graphs(k), 'YLabel'), 'String', yLabel, 'Interpreter', 'none'); end + curData = sample_data.variables{vars(k)}.data; + curDepth = depth.data; + + [nSamples, nBins] = size(curData); + if strcmpi(mode, 'timeSeries') && nBins > 1 + % ADCP data, we look for vertical dimension + iVertDim = sample_data.variables{vars(k)}.dimensions(2); + curDepth = repmat(curDepth, 1, nBins) - repmat(sample_data.dimensions{iVertDim}.data', nSamples, 1); + end + + if iExtraVar + curData = [curData(:); extra_sample_data.variables{iExtraVar}.data(:)]; + + [nSamples, nBins] = size(extra_sample_data.variables{iExtraVar}.data); + if strcmpi(mode, 'timeSeries') && nBins > 1 + % ADCP data, we look for vertical dimension + iVertDim = extra_sample_data.variables{iExtraVar}.dimensions(2); + extraDepth = repmat(extraDepth, 1, nBins) - repmat(extra_sample_data.dimensions{iVertDim}.data', nSamples, 1); + end + curDepth = [curDepth(:); extraDepth(:)]; + end + + iGood = curDepth >= 0; + if sample_data.meta.level == 1 && strcmp(func2str(plotFunc), 'graphDepthProfileGeneric') qcSet = str2double(readProperty('toolbox.qc_set')); goodFlag = imosQCFlag('good', qcSet, 'flag'); @@ -162,50 +205,39 @@ rawFlag = imosQCFlag('raw', qcSet, 'flag'); % set x and y limits so that axis are optimised for good/probably good/raw data only - curData = sample_data.variables{vars(k)}.data; - curDepth = depth.data; - curFlag = sample_data.variables{vars(k)}.flags; + curFlag = sample_data.variables{vars(k)}.flags; + if iExtraVar + curFlag = [curFlag(:); extra_sample_data.variables{iExtraVar}.flags(:)]; + end - iGood = curDepth >= 0; if strcmpi(mode, 'timeSeries') iGood = (curFlag == goodFlag) | (curFlag == pGoodFlag) | (curFlag == rawFlag); end - - [nSamples, nBins] = size(curData); - if strcmpi(mode, 'timeSeries') && nBins > 1 - % ADCP data, we look for vertical dimension - iVertDim = sample_data.variables{vars(k)}.dimensions(2); - curDepth = repmat(curDepth, 1, nBins) - repmat(sample_data.dimensions{iVertDim}.data', nSamples, 1); - end - - yLimits = [min(floor(min(curDepth(iGood))*10)/10, yLimits(1)), max(ceil(max(curDepth(iGood))*10)/10, yLimits(2))]; - xLimits = [floor(min(curData(iGood))*10)/10, ceil(max(curData(iGood))*10)/10]; - - %check for my surface soak flags - and set xLimits to flag range - if ismember(name, {'tempSoakStatus', 'cndSoakStatus', 'oxSoakStatus'}) - xLimits = [min(imosQCFlag('flag', qcSet, 'values')) max(imosQCFlag('flag', qcSet, 'values'))]; - end - - %check for xLimits max=min - if diff(xLimits) == 0; - if xLimits(1) == 0 - xLimits = [-1, 1]; - else - eps = 0.01 * xLimits(1); - xLimits = [xLimits(1) - eps, xLimits(1) + eps]; - end - end - - if any(any(iGood)) - set(graphs(k), 'YLim', yLimits); - set(graphs(k), 'XLim', xLimits); - end end - yLimits = get(graphs(k), 'YLim'); - yStep = (yLimits(2) - yLimits(1)) / 5; - yTicks = yLimits(1):yStep:yLimits(2); - set(graphs(k), 'YTick', yTicks); + yLimits = [min(floor(min(curDepth(iGood))*10)/10, yLimits(1)), max(ceil(max(curDepth(iGood))*10)/10, yLimits(2))]; + xLimits = [floor(min(curData(iGood))*10)/10, ceil(max(curData(iGood))*10)/10]; + + %check for my surface soak flags - and set xLimits to flag range + if ismember(name, {'tempSoakStatus', 'cndSoakStatus', 'oxSoakStatus'}) + xLimits = [min(imosQCFlag('flag', qcSet, 'values')) max(imosQCFlag('flag', qcSet, 'values'))]; + end + + %check for xLimits max=min + if diff(xLimits) == 0; + if xLimits(1) == 0 + xLimits = [-1, 1]; + else + eps = 0.01 * xLimits(1); + xLimits = [xLimits(1) - eps, xLimits(1) + eps]; + end + end + set(graphs(k), 'XLim', xLimits); end + + % update all graphs with greater Y range found over each variable + yStep = (yLimits(2) - yLimits(1)) / 5; + yTicks = yLimits(1):yStep:yLimits(2); + set(graphs, 'YLim', yLimits, 'YTick', yTicks); end diff --git a/Graph/graphTimeSeries.m b/Graph/graphTimeSeries.m index 689394f9b..54355fe39 100644 --- a/Graph/graphTimeSeries.m +++ b/Graph/graphTimeSeries.m @@ -1,4 +1,4 @@ -function [graphs, lines, vars] = graphTimeSeries( parent, sample_data, vars ) +function [graphs, lines, vars] = graphTimeSeries( parent, sample_data, vars, extra_sample_data ) %GRAPHTIMESERIES Graphs the given data in a time series style using subplots. % % Graphs the selected variables from the given data set. Each variable is @@ -8,7 +8,8 @@ % Inputs: % parent - handle to the parent container. % sample_data - struct containing sample data. -% vars - Indices of variables that should be graphed.. +% vars - Indices of variables that should be graphed. +% extra_sample_data - struct containing extra sample data. % % Outputs: % graphs - A vector of handles to axes on which the data has @@ -51,22 +52,23 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(3,3); + narginchk(4,4); - if ~ishandle(parent), error('parent must be a handle'); end - if ~isstruct(sample_data), error('sample_data must be a struct'); end - if ~isnumeric(vars), error('vars must be a numeric'); end + if ~ishandle(parent), error('parent must be a handle'); end + if ~isstruct(sample_data), error('sample_data must be a struct'); end + if ~isnumeric(vars), error('vars must be a numeric'); end + if ~isstruct(extra_sample_data) && ... + ~isempty(extra_sample_data), error('extra_sample_data must be a struct or empty'); end graphs = []; lines = []; if isempty(vars) - return; + return; end % get the toolbox execution mode mode = readProperty('toolbox.mode'); - switch mode case 'profile' % we don't want to plot TIME, PROFILE, DIRECTION, LATITUDE, LONGITUDE, BOT_DEPTH @@ -81,12 +83,18 @@ sample_data.variables = sample_data.variables(vars); lenVar = length(sample_data.variables); + if ~isempty(extra_sample_data) + nInst = 2; + else + nInst = 1; + end + if verLessThan('matlab','8.1') %R2013a graphs = nan(lenVar, 1); - lines = nan(lenVar, 1); + lines = nan(lenVar, nInst); else graphs = gobjects(lenVar, 1); - lines = gobjects(lenVar, 1); + lines = gobjects(lenVar, nInst); end iTimeDim = getVar(sample_data.dimensions, 'TIME'); @@ -102,6 +110,11 @@ name = sample_data.variables{k}.name; dims = sample_data.variables{k}.dimensions; + iExtraVar = 0; + if ~isempty(extra_sample_data) + iExtraVar = getVar(extra_sample_data.variables, name); + end + if length(dims) == 1 % 1D display plotFunc = @graphTimeSeriesGeneric; @@ -112,13 +125,11 @@ switch func2str(plotFunc) case 'graphTimeSeriesGeneric' varData = sample_data.variables{k}.data; + if iExtraVar + varData = [varData; extra_sample_data.variables{iExtraVar}.data]; + end if ischar(varData), varData = str2num(varData); end % we assume data is an array of one single character - minData = min(varData); - maxData = max(varData); - yLimits = [floor(minData*10)/10, ... - ceil(maxData*10)/10]; - if sample_data.meta.level == 1 qcSet = str2double(readProperty('toolbox.qc_set')); goodFlag = imosQCFlag('good', qcSet, 'flag'); @@ -126,18 +137,21 @@ rawFlag = imosQCFlag('raw', qcSet, 'flag'); % set x and y limits so that axis are optimised for good/probably good/raw data only - iGood = sample_data.variables{k}.flags == goodFlag; - iGood = iGood | (sample_data.variables{k}.flags == pGoodFlag); - iGood = iGood | (sample_data.variables{k}.flags == rawFlag); + varFlags = sample_data.variables{k}.flags; + if iExtraVar + varFlags = [varFlags; extra_sample_data.variables{iExtraVar}.flags]; + end + iGood = (varFlags == goodFlag) | (varFlags == pGoodFlag) | (varFlags == rawFlag); if any(iGood) - varData = sample_data.variables{k}.data(iGood); + varData = varData(iGood); if ischar(varData), varData = str2num(varData); end % we assume data is an array of one single character - - minData = min(varData); - maxData = max(varData); - yLimits = [floor(minData*10)/10, ceil(maxData*10)/10]; end end + + minData = min(varData); + maxData = max(varData); + yLimits = [floor(minData*10)/10, ceil(maxData*10)/10]; + case 'graphTimeSeriesTimeDepth' iZDim = sample_data.variables{k}.dimensions(2); yLimits = [floor(min(sample_data.dimensions{iZDim}.data)*10)/10, ... @@ -171,32 +185,34 @@ % create the axes graphs(k) = subplot(lenVar, 1, k, ... - 'Parent', parent, ... - 'XGrid', 'on', ... - 'Color', 'none', ... - 'YGrid', 'on', ... - 'Layer', 'top', ... - 'XLim', xLimits, ... - 'XTick', xTicks, ... - 'XTickLabel', xTickLabels, ... - 'YLim', yLimits, ... - 'YTick', yTicks); - - % make sure line colour alternate; because we are creating - % multiple axes, this is not done automatically for us - col = get(graphs(k), 'ColorOrder'); - - % we get rid of the red color which is used for global range boundaries - % and in/out water boundaries - iRed = (col == repmat([1 0 0], [size(col, 1), 1])); - iRed = sum(iRed, 2); - iRed = iRed == 3; - col(iRed, :) = []; - - col = col(mod(vars(k),length(col))+1,:); + 'Parent', parent, ... + 'XGrid', 'on', ... + 'Color', 'none', ... + 'YGrid', 'on', ... + 'Layer', 'top', ... + 'XLim', xLimits, ... + 'XTick', xTicks, ... + 'XTickLabel', xTickLabels, ... + 'YLim', yLimits, ... + 'YTick', yTicks); % plot the variable - [lines(k,:), labels] = plotFunc(graphs(k), sample_data, k, col, xTickProp); + if iExtraVar + if extra_sample_data.meta.level == 1 + qcSet = str2double(readProperty('toolbox.qc_set')); + goodFlag = imosQCFlag('good', qcSet, 'flag'); + pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); + rawFlag = imosQCFlag('raw', qcSet, 'flag'); + + iGoodExtra = (extra_sample_data.variables{iExtraVar}.flags == goodFlag) | ... + (extra_sample_data.variables{iExtraVar}.flags == pGoodFlag) | ... + (extra_sample_data.variables{iExtraVar}.flags == rawFlag); + extra_sample_data.variables{iExtraVar}.data(~iGoodExtra) = NaN; + end + [lines(k,2), ~] = plotFunc(graphs(k), extra_sample_data, iExtraVar, 'k', xTickProp); % extra instrument is always plotted in black + end + % we plot the current instrument last so that it appears on top + [lines(k,1), labels] = plotFunc(graphs(k), sample_data, k, 'b', xTickProp); % current instrument is always plotted in blue if ~isempty(labels) % set x labels and ticks diff --git a/Graph/graphTransect.m b/Graph/graphTransect.m index 3b8163905..e467a0065 100644 --- a/Graph/graphTransect.m +++ b/Graph/graphTransect.m @@ -1,10 +1,11 @@ -function [graphs lines vars] = graphTransect( parent, sample_data, vars ) +function [graphs, lines, vars] = graphTransect( parent, sample_data, vars, extra_sample_data ) %GRAPHTRANSECT Graphs the given data in a 2D transect manner, using subplot. % % Inputs: % parent - handle to the parent container. % sample_data - struct containing sample data. -% vars - Indices of variables that should be graphed.. +% vars - Indices of variables that should be graphed. +% extra_sample_data - struct containing extra sample data. % % Outputs: % graphs - A vector of handles to axes on which the data has @@ -47,11 +48,13 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(3,3); + narginchk(4,4); - if ~ishandle( parent), error('parent must be a handle'); end - if ~isstruct( sample_data), error('sample_data must be a struct'); end - if ~isnumeric(vars), error('vars must be a numeric'); end + if ~ishandle( parent), error('parent must be a handle'); end + if ~isstruct( sample_data), error('sample_data must be a struct'); end + if ~isnumeric(vars), error('vars must be a numeric'); end + if ~isstruct(extra_sample_data) && ... + ~isempty(extra_sample_data), error('extra_sample_data must be a struct or empty'); end graphs = []; lines = []; @@ -89,13 +92,13 @@ set(graphs(k), 'Parent', parent,... 'XGrid', 'on',... - 'Color', 'none',... + 'Color', 'none',... 'YGrid', 'on', ... 'ZGrid', 'on'); % plot the variable - plotFunc = getGraphFunc('Transect', 'graph', name); - [lines(k,:) labels] = plotFunc( graphs(k), sample_data, vars(k)); + plotFunc = getGraphFunc('Transect', 'graph', name); + [lines(k,:), labels] = plotFunc(graphs(k), sample_data, vars(k)); % set labels set(get(graphs(k), 'XLabel'), 'String', labels{1}, 'Interpreter', 'none'); diff --git a/Graph/graphXvY.m b/Graph/graphXvY.m index f58023cd8..635316325 100644 --- a/Graph/graphXvY.m +++ b/Graph/graphXvY.m @@ -1,4 +1,4 @@ -function [graphs, lines, vars] = graphXvY( parent, sample_data, vars ) +function [graphs, lines, vars] = graphXvY( parent, sample_data, vars, extra_sample_data ) %GRAPHTRANSECT Graphs the two variables selected from the given data set % against each other on an X-Y axis. % @@ -6,6 +6,7 @@ % parent - handle to the parent container. % sample_data - struct containing sample data. % vars - Indices of variables that should be graphed. +% extra_sample_data - struct containing extra sample data. % % Outputs: % graphs - A vector of handles to the axes on which the data has @@ -46,11 +47,13 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(3,3); + narginchk(4,4); - if ~ishandle( parent), error('parent must be a handle'); end - if ~isstruct( sample_data), error('sample_data must be a struct'); end - if ~isnumeric(vars), error('vars must be a numeric'); end + if ~ishandle( parent), error('parent must be a handle'); end + if ~isstruct( sample_data), error('sample_data must be a struct'); end + if ~isnumeric(vars), error('vars must be a numeric'); end + if ~isstruct(extra_sample_data) && ... + ~isempty(extra_sample_data), error('extra_sample_data must be a struct or empty'); end graphs = []; lines = []; @@ -73,14 +76,16 @@ end vars = vars + p; - if length(sample_data.variables{vars(1)}.dimensions) > 1 ... - || length(sample_data.variables{vars(2)}.dimensions) > 1 - error('XvY only supports single dimensional data'); - end - xname = sample_data.variables{vars(1)}.name; yname = sample_data.variables{vars(2)}.name; + ixExtraVar = 0; + iyExtraVar = 0; + if ~isempty(extra_sample_data) + ixExtraVar = getVar(extra_sample_data.variables, xname); + iyExtraVar = getVar(extra_sample_data.variables, yname); + end + % create the axes graphs = axes('Parent', parent,... 'XGrid', 'on',... @@ -89,10 +94,28 @@ 'ZGrid', 'on'); % plot the variable - plotFunc = getGraphFunc('XvY', 'graph', xname); - [lines, labels] = plotFunc(graphs, sample_data, vars); - - set(lines, 'Color', 'blue'); + plotFunc = getGraphFunc('XvY', 'graph', xname); + if ixExtraVar && iyExtraVar + if extra_sample_data.meta.level == 1 + qcSet = str2double(readProperty('toolbox.qc_set')); + goodFlag = imosQCFlag('good', qcSet, 'flag'); + pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); + rawFlag = imosQCFlag('raw', qcSet, 'flag'); + + iGoodExtra = (extra_sample_data.variables{ixExtraVar}.flags == goodFlag) | ... + (extra_sample_data.variables{ixExtraVar}.flags == pGoodFlag) | ... + (extra_sample_data.variables{ixExtraVar}.flags == rawFlag); + extra_sample_data.variables{ixExtraVar}.data(~iGoodExtra) = NaN; + + iGoodExtra = (extra_sample_data.variables{iyExtraVar}.flags == goodFlag) | ... + (extra_sample_data.variables{iyExtraVar}.flags == pGoodFlag) | ... + (extra_sample_data.variables{iyExtraVar}.flags == rawFlag); + extra_sample_data.variables{iyExtraVar}.data(~iGoodExtra) = NaN; + end + plotFunc(graphs, extra_sample_data, [ixExtraVar, iyExtraVar], 'k'); % extra instrument is always plotted in black + end + % we plot the current instrument last so that it appears on top + [lines, labels] = plotFunc(graphs, sample_data, vars, 'b'); % current instrument is always plotted in blue % set x label uom = ''; @@ -122,6 +145,13 @@ curFlagX = sample_data.variables{vars(1)}.flags; curFlagY = sample_data.variables{vars(2)}.flags; + if ixExtraVar && iyExtraVar + curDataX = [curDataX; extra_sample_data.variables{ixExtraVar}.data]; + curDataY = [curDataY; extra_sample_data.variables{iyExtraVar}.data]; + curFlagX = [curFlagX; extra_sample_data.variables{ixExtraVar}.flags]; + curFlagY = [curFlagY; extra_sample_data.variables{iyExtraVar}.flags]; + end + curFlag = max(curFlagX, curFlagY); iGood = (curFlag == goodFlag) | (curFlag == pGoodFlag) | (curFlag == rawFlag); diff --git a/Graph/lineCastVar.m b/Graph/lineCastVar.m index 693b2ac16..902d797b0 100644 --- a/Graph/lineCastVar.m +++ b/Graph/lineCastVar.m @@ -274,7 +274,7 @@ function lineCastVar(sample_data, varNames, isQC, saveToFile, exportDir) 'Layer', 'top'); % set background to be grey - set(hAxCastVar, 'Color', [0.75 0.75 0.75]) + set(hAxCastVar, 'Color', [0.85 0.85 0.85]) end end diff --git a/Graph/lineMooring1DVar.m b/Graph/lineMooring1DVar.m index 0ff97d79c..1b6c78248 100644 --- a/Graph/lineMooring1DVar.m +++ b/Graph/lineMooring1DVar.m @@ -128,7 +128,7 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) initiateFigure = true; isPlottable = false; -backgroundColor = [0.75 0.75 0.75]; +backgroundColor = [0.85 0.85 0.85]; for i=1:lenSampleData % instrument description diff --git a/Graph/lineMooring2DVarSection.m b/Graph/lineMooring2DVarSection.m index ef5ede303..b1c27197d 100644 --- a/Graph/lineMooring2DVarSection.m +++ b/Graph/lineMooring2DVarSection.m @@ -108,7 +108,7 @@ function lineMooring2DVarSection(sample_data, varName, timeValue, isQC, saveToFi dimTitle = imosParameters(dimName, 'long_name'); dimUnit = imosParameters(dimName, 'uom'); -backgroundColor = [0.75 0.75 0.75]; +backgroundColor = [0.85 0.85 0.85]; if iVar > 0 if initiateFigure diff --git a/Graph/scatterMooring1DVarAgainstDepth.m b/Graph/scatterMooring1DVarAgainstDepth.m index 9224fba59..4d3641d7b 100644 --- a/Graph/scatterMooring1DVarAgainstDepth.m +++ b/Graph/scatterMooring1DVarAgainstDepth.m @@ -156,7 +156,7 @@ function scatterMooring1DVarAgainstDepth(sample_data, varName, isQC, saveToFile, end end -backgroundColor = [0.75 0.75 0.75]; +backgroundColor = [0.85 0.85 0.85]; if any(isPlottable) % collect visualQC config @@ -265,7 +265,13 @@ function scatterMooring1DVarAgainstDepth(sample_data, varName, isQC, saveToFile, depth = sample_data{iSort(i)}.variables{iDepth}.data; else if isfield(sample_data{iSort(i)}, 'instrument_nominal_depth') - depth = sample_data{iSort(i)}.instrument_nominal_depth*ones(size(iGood)); + if ~isempty(sample_data{iSort(i)}.instrument_nominal_depth) + depth = sample_data{iSort(i)}.instrument_nominal_depth*ones(size(iGood)); + else + fprintf('%s\n', ['Error : in ' sample_data{iSort(i)}.toolbox_input_file ... + ', global attribute instrument_nominal_depth is not documented.']); + continue; + end else fprintf('%s\n', ['Error : in ' sample_data{iSort(i)}.toolbox_input_file ... ', global attribute instrument_nominal_depth is not documented.']); diff --git a/Graph/scatterMooring2DVarAgainstDepth.m b/Graph/scatterMooring2DVarAgainstDepth.m index 1be431007..a702ecf80 100644 --- a/Graph/scatterMooring2DVarAgainstDepth.m +++ b/Graph/scatterMooring2DVarAgainstDepth.m @@ -348,7 +348,18 @@ function scatterMooring2DVarAgainstDepth(sample_data, varName, isQC, saveToFile, dataDepth = sample_data{iSort(i)}.variables{iDepth}.data; else if isfield(sample_data{iSort(i)}, 'instrument_nominal_depth') - dataDepth = sample_data{iSort(i)}.instrument_nominal_depth*ones(size(iGoodTime)); + if ~isempty(sample_data{iSort(i)}.instrument_nominal_depth) + if iHeight == 0 + dataDepth = sample_data{iSort(i)}.instrument_nominal_depth*ones(size(iGoodTime)); + else + dataDepth = repmat(sample_data{iSort(i)}.instrument_nominal_depth + ... + sample_data{iSort(i)}.dimensions{iHeight}.data, 1, length(iGoodTime)); + end + else + fprintf('%s\n', ['Error : in ' sample_data{iSort(i)}.toolbox_input_file ... + ', global attribute instrument_nominal_depth is not documented.']); + continue; + end else fprintf('%s\n', ['Error : in ' sample_data{iSort(i)}.toolbox_input_file ... ', global attribute instrument_nominal_depth is not documented.']); diff --git a/Preprocessing/variableOffsetPP.m b/Preprocessing/variableOffsetPP.m index 735c6a111..a59aed564 100644 --- a/Preprocessing/variableOffsetPP.m +++ b/Preprocessing/variableOffsetPP.m @@ -92,16 +92,22 @@ for k = 1:nSampleData nVars = length(sample_data{k}.variables); - defaultOffsets{k} = zeros(nVars,1); - defaultScales{ k} = ones( nVars,1); + defaultOffsets{k} = cell(1, nVars); + defaultScales{ k} = cell(1, nVars); + + defaultOffsets{k}(:) = {'0'}; + defaultScales{ k}(:) = {'1'}; % read dataset specific PP parameters if exist and override previous entries from % parameter file depth.txt defaultOffsets{k} = readDatasetParameter(sample_data{k}.toolbox_input_file, currentPProutine, 'offset', defaultOffsets{k}); defaultScales{k} = readDatasetParameter(sample_data{k}.toolbox_input_file, currentPProutine, 'scale', defaultScales{k}); end - appliedOffsets = defaultOffsets; - appliedScales = defaultScales; + appliedOffsetsStr = defaultOffsets; + appliedScalesStr = defaultScales; + + appliedOffsetsNum = defaultOffsets; + appliedScalesNum = defaultScales; if ~auto % dialog figure @@ -164,8 +170,12 @@ timeCurrent = sample_data{k}.dimensions{iTimeCurrent}.data; nominalDepthCurrent = inf; if isfield(sample_data{k}, 'instrument_nominal_depth') - nominalDepthCurrent = sample_data{k}.instrument_nominal_depth; - else + if ~isempty(sample_data{k}.instrument_nominal_depth) + nominalDepthCurrent = sample_data{k}.instrument_nominal_depth; + end + end + + if isinf(nominalDepthCurrent) fprintf('%s\n', ['Info : ' sample_data{k}.toolbox_input_file ... ' please document instrument_nominal_depth global attributes'... ' so that a nearest instrument can be found in the mooring']); @@ -175,13 +185,20 @@ % deployment timeForDiff = timeCurrent(1) + (timeCurrent(end)-timeCurrent(1))/4; if isfield(sample_data{k}, 'time_deployment_start') - % or preferably from the moment the mooring is in position - timeForDiff = sample_data{k}.time_deployment_start; + if ~isempty(sample_data{k}.time_deployment_start) + % or preferably from the moment the mooring is in position + timeForDiff = sample_data{k}.time_deployment_start; + else + fprintf('%s\n', ['Info : ' sample_data{k}.toolbox_input_file ... + ' please document time_deployment_start global attributes'... + ' so that difference with a nearest instrument can be better calculated']); + end else fprintf('%s\n', ['Info : ' sample_data{k}.toolbox_input_file ... ' please document time_deployment_start global attributes'... ' so that difference with a nearest instrument can be better calculated']); end + iForDiff = timeCurrent >= timeForDiff & ... timeCurrent <= timeForDiff + 1; timeCurrentForDiff = timeCurrent(iForDiff); @@ -235,8 +252,8 @@ notRelevantParams = {'TIMESERIES', 'PROFILE', 'TRAJECTORIES', 'TIME', 'LATITUDE', 'LONGITUDE', 'NOMINAL_DEPTH', 'BOT_DEPTH', 'DIRECTION'}; if any(strcmpi(sample_data{k}.variables{m}.name, notRelevantParams)), continue; end - offsetVal = num2str(defaultOffsets{k}(m)); - scaleVal = num2str(defaultScales{k}(m)); + offsetVal = defaultOffsets{k}{m}; + scaleVal = defaultScales{k}{m}; sizeData = size(sample_data{k}.variables{m}.data); @@ -296,7 +313,7 @@ iNanDataCurrent = isnan(sample_data{k}.variables{m}.data); varLabel = uicontrol(... - 'Parent', setPanels(k), 'Style', 'text', 'String', sample_data{k}.variables{m}.name); + 'Parent', setPanels(k), 'Style', 'text', 'String', [sample_data{k}.variables{m}.name ' or sam.variables{' num2str(m) '}.data']); minLabel = uicontrol(... 'Parent', setPanels(k), 'Style', 'text', 'String', num2str(min(sample_data{k}.variables{m}.data(~iNanDataCurrent)))); maxLabel = uicontrol(... @@ -365,41 +382,59 @@ end % user cancelled dialog - if isempty(appliedOffsets) || isempty(appliedScales), return; end + if isempty(appliedOffsetsStr) || isempty(appliedScalesStr), return; end % otherwise, apply the offsets/scales for k = 1:nSampleData - vars = sample_data{k}.variables; + sam = sample_data{k}; + vars = sam.variables; - for m = 1:length(vars) - if ~isnan(appliedOffsets{k}(m)) && ~isnan(appliedScales{k}(m)) - if (appliedOffsets{k}(m) ~= 0 || appliedScales{k}(m) ~= 1) - % apply offsets and scales - vars{m}.data = appliedOffsets{k}(m) + (appliedScales{k}(m) .* vars{m}.data); - - variableOffsetComment = ['variableOffsetPP: variable values modified applying '... - 'the following formula: new_data = offset + (scale * data) with offset = ' ... - num2str(appliedOffsets{k}(m)) ' and scale = ' num2str(appliedScales{k}(m)) '.']; - - comment = vars{m}.comment; - if isempty(comment) - vars{m}.comment = variableOffsetComment; - else - vars{m}.comment = [comment ' ' variableOffsetComment]; - end - - history = sample_data{k}.history; - if isempty(history) - sample_data{k}.history = sprintf('%s - %s', datestr(now_utc, readProperty('exportNetCDF.dateFormat')), variableOffsetComment); - else - sample_data{k}.history = sprintf('%s\n%s - %s', history, datestr(now_utc, readProperty('exportNetCDF.dateFormat')), variableOffsetComment); - end + for m = 1:length(vars) + appliedOffsetsNum{k}{m} = str2double(appliedOffsetsStr{k}{m}); + if isnan(appliedOffsetsNum{k}{m}) + % we have a string value which is a valid Matlab formula + appliedOffsetsNum{k}{m} = eval(appliedOffsetsStr{k}{m}); + end + + appliedScalesNum{k}{m} = str2double(appliedScalesStr{k}{m}); + if isnan(appliedOffsetsNum{k}{m}) + % we have a string value which is a valid Matlab formula + appliedScalesNum{k}{m} = eval(appliedScalesStr{k}{m}); + end + + if any(any(appliedOffsetsNum{k}{m} ~= 0)) || any(any(appliedScalesNum{k}{m} ~= 1)) + % apply offsets and scales + vars{m}.data = appliedOffsetsNum{k}{m} + (appliedScalesNum{k}{m} .* vars{m}.data); + + variableOffsetComment = ['variableOffsetPP: ' vars{m}.name ' values modified '... + 'following new_data = offset + (scale * data) with offset = ' ... + appliedOffsetsStr{k}{m} ' and scale = ' appliedScalesStr{k}{m} '.']; + + % replace Matlab sam structure with actual variable names in comment + variableOffsetComment = strrep(variableOffsetComment, 'sam.', ''); + for n = 1:length(vars) + variableOffsetComment = strrep(variableOffsetComment, ['variables{' num2str(n) '}.data'], sam.variables{n}.name); + variableOffsetComment = strrep(variableOffsetComment, ['variables{' num2str(n) '}'], sam.variables{n}.name); end - if (appliedOffsets{k}(m) ~= defaultOffsets{k}(m) || appliedScales{k}(m) ~= defaultScales{k}(m)) - % write/update dataset PP parameters - writeDatasetParameter(sample_data{k}.toolbox_input_file, currentPProutine, 'offset', appliedOffsets{k}); - writeDatasetParameter(sample_data{k}.toolbox_input_file, currentPProutine, 'scale', appliedScales{k}); + + comment = vars{m}.comment; + if isempty(comment) + vars{m}.comment = variableOffsetComment; + else + vars{m}.comment = [comment ' ' variableOffsetComment]; end + + history = sample_data{k}.history; + if isempty(history) + sample_data{k}.history = sprintf('%s - %s', datestr(now_utc, readProperty('exportNetCDF.dateFormat')), variableOffsetComment); + else + sample_data{k}.history = sprintf('%s\n%s - %s', history, datestr(now_utc, readProperty('exportNetCDF.dateFormat')), variableOffsetComment); + end + end + if ~strcmpi(appliedOffsetsStr{k}{m}, defaultOffsets{k}{m}) || ~strcmpi(appliedScalesStr{k}{m}, defaultScales{k}{m}) + % write/update dataset PP parameters + writeDatasetParameter(sample_data{k}.toolbox_input_file, currentPProutine, 'offset', appliedOffsetsStr{k}); + writeDatasetParameter(sample_data{k}.toolbox_input_file, currentPProutine, 'scale', appliedScalesStr{k}); end end sample_data{k}.variables = vars; @@ -418,8 +453,8 @@ function keyPressCallback(source,ev) function cancelButtonCallback(source,ev) %CANCELBUTTONCALLBACK Discards user input, and closes the dialog. % - appliedOffsets = {}; - appliedScales = {}; + appliedOffsetsStr = {}; + appliedScalesStr = {}; delete(f); end @@ -431,37 +466,73 @@ function confirmButtonCallback(source,ev) function offsetFieldCallback(source, ev) %OFFSETFIELDCALLBACK Called when the user edits one of the offset fields. - % Verifies that the text entered is a number. + % Verifies that the text entered is a number or a valid Matlab statement. % - val = get(source, 'String'); + valStr = get(source, 'String'); setIdx = get(get(source, 'Parent'), 'UserData'); varIdx = get(source, 'UserData'); - val = str2double(val); + valNum = str2double(valStr); - % reset the offset value on non-numerical - % input, otherwise save the new value - if isnan(val), set(source, 'String', num2str(defaultOffsets{setIdx}(varIdx))); - else appliedOffsets{setIdx}(varIdx) = val; + if isnan(valNum) + % we have a string value + isValid = true; + try + sam = sample_data{setIdx}; + valNum = eval(valStr); + catch e + isValid = false; + end + + if isValid + % we have a valid Matlab formula + appliedOffsetsStr{setIdx}{varIdx} = valStr; + appliedOffsetsNum{setIdx}{varIdx} = valNum; + else + % we reset to default value + set(source, 'String', defaultOffsets{setIdx}{varIdx}); + end + else + % we have a numerical value + appliedOffsetsStr{setIdx}{varIdx} = valStr; + appliedOffsetsNum{setIdx}{varIdx} = valNum; end end function scaleFieldCallback(source, ev) %SCALEFIELDCALLBACK Called when the user edits one of the scale fields. - % Verifies that the text entered is a number. + % Verifies that the text entered is a number or a valid Matlab statement. % - val = get(source, 'String'); + valStr = get(source, 'String'); setIdx = get(get(source, 'Parent'), 'UserData'); varIdx = get(source, 'UserData'); - val = str2double(val); + valNum = str2double(valStr); - % reset the scale value on non-numerical - % input, otherwise save the new value - if isnan(val), set(source, 'String', num2str(defaultScales{setIdx}(varIdx))); - else appliedScales{setIdx}(varIdx) = val; + if isnan(valNum) + % we have a string value + isValid = true; + try + sam = sample_data{setIdx}; + valNum = eval(valStr); + catch e + isValid = false; + end + + if isValid + % we have a valid Matlab formula + appliedScalesStr{setIdx}{varIdx} = valStr; + appliedScalesNum{setIdx}{varIdx} = valNum; + else + % we reset to default value + set(source, 'String', defaultScales{setIdx}{varIdx}); + end + else + % we have a numerical value + appliedScalesStr{setIdx}{varIdx} = valStr; + appliedScalesNum{setIdx}{varIdx} = valNum; end end end diff --git a/Util/fastSaveas.m b/Util/fastSaveas.m index 850e1f6f1..d7fcc95f3 100644 --- a/Util/fastSaveas.m +++ b/Util/fastSaveas.m @@ -21,6 +21,8 @@ function fastSaveas( hFig, fileDestination ) end +drawnow; % Forces GUI to update itself, otherwise colorbar might be missing + % use hgexport to print to file as close as possible as what we see myStyle = hgexport('factorystyle'); myStyle.Format = 'png'; % (default is eps) diff --git a/Util/saveGraph.m b/Util/saveGraph.m deleted file mode 100644 index f97a58391..000000000 --- a/Util/saveGraph.m +++ /dev/null @@ -1,183 +0,0 @@ -function return_types = saveGraph( fig, ax ) -%SAVEGRAPH Saves the given axis to an image file. Prompts the user to select -% the location and file name. -% -% Inputs: -% fig - The main figure handle. -% ax - The axis handle to save. -% -% Author: Paul McCarthy -% Contributor: Guillaume Galibert -% - -% -% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated -% Marine Observing System (IMOS). -% All rights reserved. -% -% Redistribution and use in source and binary forms, with or without -% modification, are permitted provided that the following conditions are met: -% -% * Redistributions of source code must retain the above copyright notice, -% this list of conditions and the following disclaimer. -% * Redistributions in binary form must reproduce the above copyright -% notice, this list of conditions and the following disclaimer in the -% documentation and/or other materials provided with the distribution. -% * Neither the name of the AODN/IMOS nor the names of its contributors -% may be used to endorse or promote products derived from this software -% without specific prior written permission. -% -% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -% POSSIBILITY OF SUCH DAMAGE. -% - -% supported export types and corresponding print function switches -fileTypes = {'*.png'; '*.jpg' ; '*.bmp' ; '*.pdf'}; -printSwitches = {'-dpng'; '-djpeg'; '-dbmp16m'; '-dpdf'}; - - -dateFmt = readProperty('toolbox.timeFormat'); - -try - noPrompt = eval(readProperty('saveGraph.noPrompt')); -catch e - noPrompt = false; -end - -try - exportDir = readProperty('saveGraph.exportDir'); - if ~exist(exportDir, 'dir'), error(''); end -catch e - exportDir = '.'; -end - -try - imgType = ['*.' readProperty('saveGraph.imgType')]; - if ~ismember(imgType, fileTypes), error(''); end - imgType = find(strcmp(imgType, fileTypes)); -catch e - imgType = 1; -end - -fileName = ['graph_' datestr(now, dateFmt) '.' fileTypes{imgType}(3:end)]; - -if ~noPrompt - - while true - - % prompt user to select file type, name, save location - [fileName exportDir imgType] = ... - uiputfile(fileTypes, 'Save Graph', exportDir); - - % user cancelled dialog - if fileName == 0, return; end - - [~, name, ext] = fileparts(fileName); - - % The uiputfile function automatically adds an 'All Files' - % option to the list of file types. What purpose could this possibly - % serve? It's a save dialog. Anyway, if the user - % selects this option, we need to figure out if the user has provided a - % file extension, and if it is a supported type. - if imgType > length(fileTypes) - - % if user hasn't provided an extension, just use the default - if isempty(ext), imgType = 1; - - % if user has provided an unknown type, show an error, reprompt - elseif ~ismember(['*' ext], fileTypes) - - e = errordlg(... - 'Please provide a file type', 'Unknown file type', 'modal'); - uiwait(e); - - continue; - - % otherwise extract the extension - else imgType = find(strcmp(['*' ext], fileTypes)); - end - end - - % if user hasn't provided the correct extension, add it - if isempty(ext), fileName = [name '.' fileTypes{imgType}(3:end)]; - - % if user has provided a different - % extension, append the correct extension - elseif ~strcmp(['*' ext], fileTypes{imgType}) - - fileName = [fileName '.' fileTypes{imgType}(3:end)]; - end - - % update toolbox properties for next time - writeProperty('saveGraph.exportDir', exportDir); - writeProperty('saveGraph.imgType', fileTypes{imgType}(3:end)); - - break; - end -end - -progress = waitbar(0, 'Saving graph', ... - 'Name', 'Saving',... - 'DefaultTextInterpreter','none'); - -% Matlab can't save individual axes - it is only able to save complete -% figures. What we are doing, then, is copying the provided axis over to a -% new, invisible figure, and saving that figure. -saveFig = figure('Visible', 'off'); - -% ensure the printed version is the same whatever the screen used. -set(saveFig, 'PaperPositionMode', 'manual'); -set(saveFig, 'PaperType', 'A4', 'PaperOrientation', 'landscape', 'PaperUnits', 'normalized', 'PaperPosition', [0, 0, 1, 1]); - -% preserve the color scheme -set(saveFig, 'InvertHardcopy', 'off'); - -new_ax = copyobj(ax, saveFig); - -% Note that figure properties like the colormap and axes properties are not -% copied automatically by copyobj. -col = get(fig, 'Colormap'); -set(saveFig, 'Colormap', col); - -% the default renderer under windows is opengl; for some reason, -% printing pcolor plots fails when using opengl as the renderer -set(saveFig, 'Renderer', 'zbuffer'); - -waitbar(1, progress); -msg = ''; -icon = ''; - -try - print(saveFig, printSwitches{imgType}, fullfile(exportDir, fileName)); - - % trick to save the image in landscape rather than portrait file - image = imread(fullfile(exportDir, fileName)); - r = image(:,:,1); - g = image(:,:,2); - b = image(:,:,3); - r = rot90(r, 3); - g = rot90(g, 3); - b = rot90(b, 3); - image = cat(3, r, g, b); - imwrite(image, fullfile(exportDir, fileName)); - - msg = [fileName ' saved successfully']; - icon = 'none'; - -catch e - msg = ['Could not save ' fileName ' (' e.message ')']; - icon = 'error'; -end - -delete(saveFig); -close(progress); -uiwait(msgbox(msg, 'Save graph', icon, 'non-modal')); diff --git a/imosToolbox.m b/imosToolbox.m index 06ea2b00b..1d1b12f4b 100644 --- a/imosToolbox.m +++ b/imosToolbox.m @@ -49,7 +49,7 @@ function imosToolbox(auto, varargin) % % Set current toolbox version -toolboxVersion = ['2.5.30 - ' computer]; +toolboxVersion = ['2.5.31 - ' computer]; if nargin == 0, auto = 'manual'; end diff --git a/imosToolbox_Linux64.bin b/imosToolbox_Linux64.bin index 776d1d058..e2980a022 100755 Binary files a/imosToolbox_Linux64.bin and b/imosToolbox_Linux64.bin differ diff --git a/imosToolbox_Win32.exe b/imosToolbox_Win32.exe index 9dfce4055..f653fd3d5 100644 Binary files a/imosToolbox_Win32.exe and b/imosToolbox_Win32.exe differ diff --git a/imosToolbox_Win64.exe b/imosToolbox_Win64.exe index 615b743cc..e31e8f24d 100644 Binary files a/imosToolbox_Win64.exe and b/imosToolbox_Win64.exe differ