diff --git a/AutomaticQC/imosEchoIntensityVelocitySetQC.m b/AutomaticQC/imosEchoIntensityVelocitySetQC.m index a5dab5cf9..6a857dba7 100644 --- a/AutomaticQC/imosEchoIntensityVelocitySetQC.m +++ b/AutomaticQC/imosEchoIntensityVelocitySetQC.m @@ -65,9 +65,9 @@ idWcur = 0; idCspd = 0; idCdir = 0; -idABSI = cell(4, 1); +idABSIC = cell(4, 1); for j=1:4 - idABSI{j} = 0; + idABSIC{j} = 0; end lenVar = length(sample_data.variables); for i=1:lenVar @@ -80,21 +80,21 @@ if strncmpi(paramName, 'CDIR', 4), idCdir = i; end for j=1:4 cc = int2str(j); - if strcmpi(paramName, ['ABSI' cc]), idABSI{j} = i; end + if strcmpi(paramName, ['ABSIC' cc]), idABSIC{j} = i; end end end % check if the data is compatible with the QC algorithm idMandatory = (idUcur | idVcur | idWcur | idCspd | idCdir); for j=1:4 - idMandatory = idMandatory & idABSI{j}; + idMandatory = idMandatory & idABSIC{j}; end if ~idMandatory, return; end % let's get the associated vertical dimension -idVertDim = sample_data.variables{idABSI{1}}.dimensions(2); +idVertDim = sample_data.variables{idABSIC{1}}.dimensions(2); if strcmpi(sample_data.dimensions{idVertDim}.name, 'DIST_ALONG_BEAMS') - disp(['Warning : imosEchoIntensityVelocitySetQC applied with a non tilt-corrected ABSIn (no bin mapping) on dataset ' sample_data.toolbox_input_file]); + disp(['Warning : imosEchoIntensityVelocitySetQC applied with a non tilt-corrected ABSICn (no bin mapping) on dataset ' sample_data.toolbox_input_file]); end qcSet = str2double(readProperty('toolbox.qc_set')); @@ -103,10 +103,10 @@ rawFlag = imosQCFlag('raw', qcSet, 'flag'); %Pull out echo intensity -sizeData = size(sample_data.variables{idABSI{1}}.data); +sizeData = size(sample_data.variables{idABSIC{1}}.data); ea = nan(4, sizeData(1), sizeData(2)); -for j=1:4; - ea(j, :, :) = sample_data.variables{idABSI{j}}.data; +for j=1:4 + ea(j, :, :) = sample_data.variables{idABSIC{j}}.data; end % read in filter parameters diff --git a/AutomaticQC/imosInOutWaterQC.m b/AutomaticQC/imosInOutWaterQC.m index 98aa847be..410fc145d 100644 --- a/AutomaticQC/imosInOutWaterQC.m +++ b/AutomaticQC/imosInOutWaterQC.m @@ -1,4 +1,4 @@ -function [data flags paramsLog] = imosInOutWaterQC( sample_data, data, k, type, auto ) +function [data, flags, paramsLog] = imosInOutWaterQC( sample_data, data, k, type, auto ) %IMOSINOUTWATERQC Flags samples which were taken before and after the instrument was placed % in the water. % @@ -110,6 +110,29 @@ end case 'timeSeries' + % for test in display + sampleFile = sample_data.toolbox_input_file; + + mWh = findobj('Tag', 'mainWindow'); + qcParam = get(mWh, 'UserData'); + p = 0; + if ~isempty(qcParam) + for i=1:length(qcParam) + if strcmp(qcParam(i).dataSet, sampleFile) + p = i; + break; + end + end + end + if p == 0 + p = length(qcParam) + 1; + end + qcParam(p).dataSet = sampleFile; + qcParam(p).('inWater') = time_in_water; + qcParam(p).('outWater') = time_out_water; + % update qcParam for display + set(mWh, 'UserData', qcParam); + qcSet = str2double(readProperty('toolbox.qc_set')); rawFlag = imosQCFlag('raw', qcSet, 'flag'); failFlag = imosQCFlag('bad', qcSet, 'flag'); diff --git a/DDB/executeDDBQuery.m b/DDB/executeDDBQuery.m index facedbc8d..d0a0953dc 100644 --- a/DDB/executeDDBQuery.m +++ b/DDB/executeDDBQuery.m @@ -92,10 +92,13 @@ % execute the query - the java method returns % an ArrayList of org.imos.ddb.schema.* objects. + source = ''; + driver = ''; connection = ''; dbuser = ''; dbpassword = ''; try + source = readProperty('toolbox.ddb'); driver = readProperty('toolbox.ddb.driver'); connection = readProperty('toolbox.ddb.connection'); dbuser = readProperty('toolbox.ddb.user'); @@ -104,7 +107,7 @@ end if isempty(connection) - ddb = org.imos.ddb.DDB.getDDB(readProperty('toolbox.ddb')); + ddb = org.imos.ddb.DDB.getDDB(source); else ddb = org.imos.ddb.DDB.getDDB(driver, connection, dbuser, dbpassword); end diff --git a/FlowManager/displayManager.m b/FlowManager/displayManager.m index eb57022eb..19a4a0695 100644 --- a/FlowManager/displayManager.m +++ b/FlowManager/displayManager.m @@ -248,7 +248,7 @@ function rawDataCallback() graphs = []; try graphFunc = getGraphFunc(graphType, 'graph', ''); - [graphs lines vars] = graphFunc(panel, sample_data{setIdx}, vars); + [graphs, lines, vars] = graphFunc(panel, sample_data{setIdx}, vars); catch e errorString = getErrorString(e); fprintf('%s\n', ['Error says : ' errorString]); @@ -264,7 +264,7 @@ function rawDataCallback() if ~isempty(graphs) for k = 1:length(graphs) - set(graphs(k), 'UserData', {lines(k), k}); + set(graphs(k), 'UserData', {lines(k,:), k}); end end @@ -330,7 +330,7 @@ function qcDataCallback() flagFunc = []; end - [graphs lines vars] = graphFunc(panel, sample_data{setIdx}, vars); + [graphs, lines, vars] = graphFunc(panel, sample_data{setIdx}, vars); if isempty(flagFunc) warning(['Cannot display QC flags using ' graphType ... @@ -355,7 +355,7 @@ function qcDataCallback() % so the data select callback can retrieve them for k = 1:length(graphs) - set(graphs(k), 'UserData', {lines(k), k}); + set(graphs(k), 'UserData', {lines(k,:), k}); end % add data selection functionality diff --git a/GUI/mainWindow.m b/GUI/mainWindow.m index 022e33ba7..5ecce1843 100644 --- a/GUI/mainWindow.m +++ b/GUI/mainWindow.m @@ -498,9 +498,10 @@ function zoomPostCallback(source,ev) for i=1:length(graphs1D) userData = get(graphs1D(i), 'UserData'); hData = userData{1}; - iTickBox = userData{2}; + iTickBox = userData{2}; % index into currently ticked boxes + iAllTickedBox = find(cell2mat(get(hTickBoxes, 'Value')) == 1); - varName = get(hTickBoxes(iTickBox), 'String'); + varName = get(hTickBoxes(iAllTickedBox(iTickBox)), 'String'); iVar = getVar(sam.variables, varName); flags = sam.variables{iVar}.flags; iGood = ismember(flags, okFlags); @@ -578,24 +579,21 @@ function displayLineMooringVar(source,ev, isQC) stringQC = 'non QC'; if isQC, stringQC = 'QC'; end - % get all params that are in common in at least two datasets 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 - if i==1 && j==1 - paramsName{1} = sample_data{1}.variables{1}.name; - paramsCount(1) = 1; + 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 - 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 + paramsCount(sameParam) = paramsCount(sameParam)+1; end end end @@ -622,14 +620,14 @@ function displayLineMooringVar(source,ev, isQC) iParam = strcmpi(paramsName, 'NOMINAL_DEPTH'); paramsName(iParam) = []; - % by default TEMP is selected - iTEMP = find(strcmpi(paramsName, 'TEMP')); + % by default diff(TIME) is selected + iDiffTIME = find(strcmpi(paramsName, 'diff(TIME)')); [iSelection, ok] = listdlg(... 'ListString', paramsName, ... 'SelectionMode', 'single', ... 'ListSize', [150 150], ... - 'InitialValue', iTEMP, ... + 'InitialValue', iDiffTIME, ... 'Name', ['Plot a ' stringQC '''d variable accross all instruments in the mooring'], ... 'PromptString', 'Select a variable :'); @@ -949,68 +947,84 @@ 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; + 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; end % retrieve x/y click positions + data index posClic = get(event_obj, 'Position'); I = get(event_obj, 'DataIndex'); - switch graph - case 'Profile' - dimLabel = 'DEPTH'; - dimUnit = ' m'; - dimFun = @num2str; - case 'TimeSeries' - dimLabel = 'TIME'; - dimUnit = ' UTC'; - dimFun = @datestr; - otherwise - error(['graph type ' graph ' not supported']); - end - iDim = getVar(sam.dimensions, dimLabel); nRecord = length(sam.dimensions{iDim}.data); + xLabel = get(get(gca, 'XLabel'), 'String'); + nVar = length(vars); - txt = cell(1, nVar+1); - txt{1} = [dimLabel ': ' dimFun(posClic(1)) dimUnit]; - for iVar=1:nVar - iVarCorr = vars(iVar)+varOffset; - varLabel = sam.variables{iVarCorr}.name; - varUnit = [' ' sam.variables{iVarCorr}.units]; - - nSample = numel(sam.variables{iVarCorr}.data); - iSample = I; - zInfo = ''; - if I < nRecord && nSample > nRecord - % we've clicked on a 1D plot so don't want to display - % information from 2D plots - txt{iVar+1} = []; - continue; - else - % we've clicked on a 2D plot - nDim = sam.variables{iVarCorr}.dimensions; - if nDim==1 - % and are dealing with a 1D info - iSample = sam.dimensions{iDim}.data == posClic(1); - else - % and are dealing with a 2D info - iZ = sam.variables{iVarCorr}.dimensions(2); - nZ = length(sam.dimensions{iZ}.data); - iSample = repmat(sam.dimensions{iDim}.data == posClic(1), 1, nZ) & repmat((sam.dimensions{iZ}.data == posClic(2))', nRecord, 1); + switch graph + case 'DepthProfile' + txt = cell(1, nVar+1); + % impossible to retrieve an exact TIME value, unfortunately + % DataIndex doesn't make sense + txt{1} = ['DEPTH: ' num2str(posClic(2)) ' m']; + + for iVar=1:nVar + iVarCorr = vars(iVar)+varOffset; + varLabel = sam.variables{iVarCorr}.name; + varUnit = [' ' sam.variables{iVarCorr}.units]; + + varData = num2str(posClic(1)); - zLabel = sam.dimensions{iZ}.name; - zUnit = [' ' sam.dimensions{iZ}.units]; - zData = num2str(posClic(2)); - zInfo = [' @' zLabel ': ' zData zUnit]; + if ~isempty(strfind(xLabel, varLabel)) + txt{iVar+1} = [varLabel ': ' varData varUnit]; + end + end + + case 'TimeSeries' + txt = cell(1, nVar+1); + txt{1} = [dimLabel ': ' dimFun(posClic(1)) dimUnit]; + + for iVar=1:nVar + iVarCorr = vars(iVar)+varOffset; + varLabel = sam.variables{iVarCorr}.name; + varUnit = [' ' sam.variables{iVarCorr}.units]; + + nSample = numel(sam.variables{iVarCorr}.data); + zInfo = ''; + if strcmpi(get(gca, 'Tag'), 'axis1D') && nSample > nRecord + % we've clicked on a 1D plot and are dealing with 2D information + % we don't want to display + txt{iVar+1} = []; + continue; + else + nDim = length(sam.variables{iVarCorr}.dimensions); + if nDim == 1 + % we've clicked on a 1D plot and are dealing with a 1D info + iSample = sam.dimensions{iDim}.data == posClic(1); + else + % we've clicked on a 2D plot and are dealing with a 2D info + iZ = sam.variables{iVarCorr}.dimensions(2); + nZ = length(sam.dimensions{iZ}.data); + iSample = repmat(sam.dimensions{iDim}.data == posClic(1), 1, nZ) & repmat((sam.dimensions{iZ}.data == posClic(2))', nRecord, 1); + + zLabel = sam.dimensions{iZ}.name; + zUnit = [' ' sam.dimensions{iZ}.units]; + zData = num2str(posClic(2)); + zInfo = [' @' zLabel ': ' zData zUnit]; + end + end + varData = num2str(sam.variables{iVarCorr}.data(iSample)); + + txt{iVar+1} = [varLabel ': ' varData varUnit zInfo]; end - end - varData = num2str(sam.variables{iVarCorr}.data(iSample)); - - txt{iVar+1} = [varLabel ': ' varData varUnit zInfo]; end % clean up empty cells diff --git a/GUI/startDialog.m b/GUI/startDialog.m index 642bb1fb3..640ed517f 100644 --- a/GUI/startDialog.m +++ b/GUI/startDialog.m @@ -123,7 +123,11 @@ % % dialog figure - f = figure('Name', 'Select Field Trip', ... + source = readProperty('toolbox.ddb'); + if isempty(source) + source = readProperty('toolbox.ddb.connection'); + end + f = figure('Name', ['Select Field Trip - ' source], ... 'Visible', 'off',... 'MenuBar', 'none',... 'Resize', 'off',... diff --git a/Graph/DepthProfile/flagDepthProfileGeneric.m b/Graph/DepthProfile/flagDepthProfileGeneric.m index 41d23b602..6fdc4b1c0 100644 --- a/Graph/DepthProfile/flagDepthProfileGeneric.m +++ b/Graph/DepthProfile/flagDepthProfileGeneric.m @@ -57,6 +57,9 @@ qcSet = str2double(readProperty('toolbox.qc_set')); rawFlag = imosQCFlag('raw', qcSet, 'flag'); +% get the toolbox execution mode +mode = readProperty('toolbox.mode'); + % find the depth data, either a variable or dimension depth = getVar(sample_data.variables, 'DEPTH'); @@ -70,9 +73,17 @@ depth = sample_data.dimensions{depth}; end -dim = depth.data; -fl = sample_data.variables{var}.flags; data = sample_data.variables{var}.data; +fl = sample_data.variables{var}.flags; + +[nSamples, nBins] = size(data); +if strcmpi(mode, 'timeSeries') && nBins > 1 + % ADCP data, we look for vertical dimension + iVertDim = sample_data.variables{var}.dimensions(2); + dim = repmat(depth.data, 1, nBins) - repmat(sample_data.dimensions{iVertDim}.data', nSamples, 1); +else + dim = depth.data; +end % get a list of the different flag types to be graphed flagTypes = unique(fl); diff --git a/Graph/DepthProfile/getSelectedDepthProfileGeneric.m b/Graph/DepthProfile/getSelectedDepthProfileGeneric.m index 95c866dcc..d072368e5 100644 --- a/Graph/DepthProfile/getSelectedDepthProfileGeneric.m +++ b/Graph/DepthProfile/getSelectedDepthProfileGeneric.m @@ -1,5 +1,4 @@ -function dataIdx = getSelectedDepthProfileGeneric( ... - sample_data, var, ax, highlight, click ) +function dataIdx = getSelectedDepthProfileGeneric( sample_data, var, ax, highlight ) %GETSELECTEDDEPTHPROFILEGENERIC Returns the indices of the currently selected % (highlighted) data on the given axis. % @@ -50,22 +49,4 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % -narginchk(4,4); - -if ~isstruct(sample_data), error('sample_data must be a struct'); end -if ~isnumeric(var), error('var must be numeric'); end -if ~ishandle(ax), error('ax must be a graphics handle'); end -if ~ishandle(highlight), error('highlight must be a graphics handle'); end - -depth = getVar(sample_data.dimensions, 'DEPTH'); -if depth ~= 0 - depth = sample_data.dimensions{depth}; -else - depth = getVar(sample_data.variables, 'DEPTH'); - depth = sample_data.variables{depth}; -end - -highlightY = get(highlight, 'YData'); - -% find the indices of the selected points -dataIdx = find(ismember(depth.data, highlightY)); +dataIdx = getSelectedTimeSeriesGeneric(sample_data, var, ax, highlight); \ No newline at end of file diff --git a/Graph/DepthProfile/graphDepthProfileGeneric.m b/Graph/DepthProfile/graphDepthProfileGeneric.m index 073fed0a9..585cd3f36 100644 --- a/Graph/DepthProfile/graphDepthProfileGeneric.m +++ b/Graph/DepthProfile/graphDepthProfileGeneric.m @@ -50,6 +50,9 @@ if ~isstruct(sample_data), error('sample_data must be a struct'); end if ~isnumeric(var), error('var must be a numeric'); end +% get the toolbox execution mode +mode = readProperty('toolbox.mode'); + % look for a depth variable iDepthVar = getVar(sample_data.variables, 'DEPTH'); if iDepthVar ~= 0 @@ -66,9 +69,24 @@ var = sample_data.variables{var}; -h = line(var.data(:, 1), depth.data(:, 1), 'Parent', ax, 'LineStyle', '-'); % downcast -if size(var.data, 2) > 1 -h(end+1) = line(var.data(:, 2), depth.data(:, 2), 'Parent', ax, 'LineStyle', '--'); %upcast +switch mode + case 'profile' + h = line(var.data(:, 1), depth.data(:, 1), 'Parent', ax, 'LineStyle', '-'); % downcast + if size(var.data, 2) > 1 + h(end+1) = line(var.data(:, 2), depth.data(:, 2), 'Parent', ax, 'LineStyle', '--'); % upcast + end + + case 'timeSeries' + if size(var.data, 2) > 1 + % 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', '-'); + end + else + h = line(var.data, depth.data, 'Parent', ax, 'LineStyle', '-'); + end + end set(ax, 'Tag', 'axis1D'); @@ -80,8 +98,8 @@ if ~isempty(climatologyRange) if isfield(climatologyRange, ['rangeMin' var.name]) xLim = get(ax, 'XLim'); - hRMin = line(climatologyRange(iSample).(['rangeMin' var.name]), [depth.data(1); depth.data(end)], 'Parent', ax, 'Color', 'r'); - hRMax = line(climatologyRange(iSample).(['rangeMax' var.name]), [depth.data(1); depth.data(end)], 'Parent', ax, 'Color', 'r'); + line(climatologyRange(iSample).(['rangeMin' var.name]), [depth.data(1); depth.data(end)], 'Parent', ax, 'Color', 'r'); + line(climatologyRange(iSample).(['rangeMax' var.name]), [depth.data(1); depth.data(end)], 'Parent', ax, 'Color', 'r'); set(ax, 'XLim', xLim); end end diff --git a/Graph/DepthProfile/selectDepthProfileGeneric.m b/Graph/DepthProfile/selectDepthProfileGeneric.m index b30fd3729..593cc3bd4 100644 --- a/Graph/DepthProfile/selectDepthProfileGeneric.m +++ b/Graph/DepthProfile/selectDepthProfileGeneric.m @@ -2,7 +2,7 @@ function selectDepthProfileGeneric( selectCallback, clickCallback ) %SELECTDEPTHPROFILEGENERIC Adds callbacks to the current figure, allowing the % user to interact with data in the current depth profile axis using the mouse. % -% See Graph/TimeSeries/selectTimeSeriesGeneric for more documentation +% This function delegates to Graph/TimeSeries/selectTimeSeriesGeneric.m. % % Inputs: % selectCallback - function handle which is called when the user selects @@ -43,101 +43,4 @@ function selectDepthProfileGeneric( selectCallback, clickCallback ) % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(2,2); - - if ~isa(selectCallback, 'function_handle') - error('selectCallback must be a function handle'); - end - if ~isa(clickCallback, 'function_handle') - error('clickCallback must be a function handle'); - end - - % get handle to the current figure - f = gcf; - - % add callbacks for dragging over an area of data/flags - - % this also accounts for click events - set(f, 'WindowButtonDownFcn', @buttonDown); - set(f, 'WindowButtonMotionFcn', @buttonMove); - set(f, 'WindowButtonUpFcn', @buttonUp); - - % state variables used during dragging - drag = false; - startPoint = []; - endPoint = []; - rect = []; - rectPos = []; - - function buttonDown(source,ev) - %BUTTONDOWN Captures the coordinates when the mouse is clicked on an - % axes. - % - % bail if the user clicks another mouse button while dragging - if drag - startPoint = []; - endPoint = []; - drag = false; - delete(rect); - rect = []; - return; - end - - startPoint = get(gca, 'CurrentPoint'); - startPoint = startPoint([1 3]); - - rectPos = [startPoint 0.0001 0.0001]; - - rect = rectangle(... - 'Parent', gca,... - 'Position', rectPos,... - 'EdgeColor', [1 1 1],... - 'LineStyle', '--',... - 'LineWidth', 0.25); - - drag = true; - end - - function buttonMove(source,ev) - %BUTTONMOVE Captures the coordinates when the mouse is dragged. Draws a - % rectangle from where the mouse was clicked to the current mouse location. - % - if ~drag, return; end - - endPoint = get(gca, 'CurrentPoint'); - endPoint = endPoint([1 3]); - - rectPos([1 2]) = min(startPoint, endPoint); - rectPos([3 4]) = abs(endPoint - startPoint); - - % guard against negative/zero width/height - if ~any(rectPos([3 4]) <= 0), set(rect, 'Position', rectPos); end - end - - function buttonUp(source,ev) - %BUTTONUP Captures coordinates when the mouse button is released. - % Calls the selectCallback. - % - if ~drag, return; end - - endPoint = get(gca, 'CurrentPoint'); - endPoint = endPoint([1 3]); - - range = [min(startPoint, endPoint), max(startPoint, endPoint)]; - - type = get(gcbf, 'SelectionType'); - - % click or drag? - click = false; - if startPoint == endPoint, click = true; end - - startPoint = []; - endPoint = []; - drag = false; - delete(rect); - rect = []; - - if click, clickCallback( gca, type, startPoint); - else selectCallback(gca, type, range); - end - end -end +selectTimeSeriesGeneric( selectCallback, clickCallback ); diff --git a/Graph/TimeSeries/getSelectedTimeSeriesGeneric.m b/Graph/TimeSeries/getSelectedTimeSeriesGeneric.m index 7eeb65bb0..e0edac25d 100644 --- a/Graph/TimeSeries/getSelectedTimeSeriesGeneric.m +++ b/Graph/TimeSeries/getSelectedTimeSeriesGeneric.m @@ -1,5 +1,4 @@ -function dataIdx = getSelectedTimeSeriesGeneric( ... - sample_data, var, ax, highlight ) +function dataIdx = getSelectedTimeSeriesGeneric( sample_data, var, ax, highlight ) %GETSELECTEDTIMESERIESGENERIC Returns the indices of the currently selected % (highlighted) data on the given axis. % @@ -54,10 +53,4 @@ if ~ishandle(ax), error('ax must be a graphics handle'); end if ~ishandle(highlight), error('highlight must be a graphics handle'); end -iTimeDim = getVar(sample_data.dimensions, 'TIME'); -time = sample_data.dimensions{iTimeDim}; - -highlightX = get(highlight, 'XData'); - -% find the indices of the selected points -dataIdx = find(ismember(time.data, highlightX)); +dataIdx = get(highlight, 'UserData'); \ No newline at end of file diff --git a/Graph/TimeSeries/getSelectedTimeSeriesTimeDepth.m b/Graph/TimeSeries/getSelectedTimeSeriesTimeDepth.m index a434e61f4..9c1e23ac9 100644 --- a/Graph/TimeSeries/getSelectedTimeSeriesTimeDepth.m +++ b/Graph/TimeSeries/getSelectedTimeSeriesTimeDepth.m @@ -46,31 +46,4 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % -narginchk(4, 4); - -if ~isstruct(sample_data), error('sample_data must be a struct'); end -if ~isnumeric(var), error('var must be numeric'); end -if ~ishandle(ax), error('ax must be a graphics handle'); end -if ~ishandle(highlight), error('highlight must be a graphics handle'); end - -dataIdx = []; - -iTimeDim = getVar(sample_data.dimensions, 'TIME'); -depth = sample_data.variables{var}.dimensions(2); - -time = sample_data.dimensions{iTimeDim} .data; -depth = sample_data.dimensions{depth}.data; - -highlightX = get(highlight, 'XData'); -highlightY = get(highlight, 'YData'); - -% turn the highlight into data indices -for k = 1:length(highlightX) - - % get the indices, on each dimension, of each point in the highlight - timeIdx = find(time == highlightX(k)); - depthIdx = find(depth == highlightY(k)); - - % 'flatten' those indices - dataIdx = [dataIdx ((depthIdx - 1) * length(time) + timeIdx)]; -end \ No newline at end of file +dataIdx = getSelectedTimeSeriesGeneric(sample_data, var, ax, highlight); \ No newline at end of file diff --git a/Graph/TimeSeries/getSelectedTimeSeriesTimeFrequency.m b/Graph/TimeSeries/getSelectedTimeSeriesTimeFrequency.m index 1768510b1..1d032c641 100644 --- a/Graph/TimeSeries/getSelectedTimeSeriesTimeFrequency.m +++ b/Graph/TimeSeries/getSelectedTimeSeriesTimeFrequency.m @@ -46,31 +46,4 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % -narginchk(4, 4); - -if ~isstruct(sample_data), error('sample_data must be a struct'); end -if ~isnumeric(var), error('var must be numeric'); end -if ~ishandle(ax), error('ax must be a graphics handle'); end -if ~ishandle(highlight), error('highlight must be a graphics handle'); end - -dataIdx = []; - -iTimeDim = getVar(sample_data.dimensions, 'TIME'); -freq = sample_data.variables{var}.dimensions(4); - -time = sample_data.dimensions{iTimeDim}.data; -freq = sample_data.dimensions{freq}.data; - -highlightX = get(highlight, 'XData'); -highlightY = get(highlight, 'YData'); - -% turn the highlight into data indices -for k = 1:length(highlightX) - - % get the indices, on each dimension, of each point in the highlight - timeIdx = find(time == highlightX(k)); - freqIdx = find(freq == highlightY(k)); - - % 'flatten' those indices - dataIdx = [dataIdx ((freqIdx - 1) * length(time) + timeIdx)]; -end \ No newline at end of file +dataIdx = getSelectedTimeSeriesGeneric(sample_data, var, ax, highlight); \ No newline at end of file diff --git a/Graph/TimeSeries/graphTimeSeriesGeneric.m b/Graph/TimeSeries/graphTimeSeriesGeneric.m index ed1bc9d4e..194bb10ac 100644 --- a/Graph/TimeSeries/graphTimeSeriesGeneric.m +++ b/Graph/TimeSeries/graphTimeSeriesGeneric.m @@ -64,28 +64,34 @@ h = line(time.data, var.data, 'Parent', ax, 'Color', color); set(ax, 'Tag', 'axis1D'); -% for global/regional range display +% for global/regional range and in/out water display mWh = findobj('Tag', 'mainWindow'); sMh = findobj('Tag', 'samplePopUpMenu'); iSample = get(sMh, 'Value'); -climatologyRange = get(mWh, 'UserData'); -if ~isempty(climatologyRange) - if isfield(climatologyRange, ['rangeMin' var.name]) +qcParam = get(mWh, 'UserData'); +if ~isempty(qcParam) + if isfield(qcParam, ['rangeMin' var.name]) hold(ax, 'on'); - if (length(climatologyRange(iSample).(['rangeMin' var.name])) == 2) + if (length(qcParam(iSample).(['rangeMin' var.name])) == 2) timeToPlot = [time.data(1); time.data(end)]; else timeToPlot = time.data; end - if isfield(climatologyRange, ['range' var.name]) - hNominal = line(timeToPlot, climatologyRange(iSample).(['range' var.name]), 'Parent', ax, 'Color', 'k'); + if isfield(qcParam, ['range' var.name]) + line(timeToPlot, qcParam(iSample).(['range' var.name]), 'Parent', ax, 'Color', 'k'); end - hRegMinMax = line([timeToPlot; NaN; timeToPlot], ... - [climatologyRange(iSample).(['rangeMin' var.name]); NaN; climatologyRange(iSample).(['rangeMax' var.name])], ... + line([timeToPlot; NaN; timeToPlot], ... + [qcParam(iSample).(['rangeMin' var.name]); NaN; qcParam(iSample).(['rangeMax' var.name])], ... + 'Parent', ax, 'Color', 'r'); + end + + if isfield(qcParam, 'inWater') + hold(ax, 'on'); + yLim = get(ax, 'YLim'); + line([qcParam(iSample).inWater, qcParam(iSample).inWater, NaN, qcParam(iSample).outWater, qcParam(iSample).outWater], ... + [yLim, NaN, yLim], ... 'Parent', ax, 'Color', 'r'); -% set(ax, 'YLim', [min(climatologyRange(iSample).(['rangeMin' var.name])) - min(climatologyRange(iSample).(['rangeMin' var.name]))/10, ... -% max(climatologyRange(iSample).(['rangeMax' var.name])) + max(climatologyRange(iSample).(['rangeMax' var.name]))/10]); end end diff --git a/Graph/TimeSeries/highlightTimeSeriesGeneric.m b/Graph/TimeSeries/highlightTimeSeriesGeneric.m index 5e17afbcd..570eeb76a 100644 --- a/Graph/TimeSeries/highlightTimeSeriesGeneric.m +++ b/Graph/TimeSeries/highlightTimeSeriesGeneric.m @@ -62,15 +62,19 @@ xdata = get(data, 'XData'); ydata = get(data, 'YData'); +if iscell(xdata) + xdata = cell2mat(xdata)'; + ydata = cell2mat(ydata)'; +end + % on right click highlight, only highlight % unflagged data points in the region -if strcmp(type, 'alt') - - f = variable.flags; - f = f == 0; - - xdata = xdata(f); - ydata = ydata(f); +if strcmp(type, 'alt') + f = variable.flags; + f = f == 0; + + xdata = xdata(f); + ydata = ydata(f); end % figure out indices of all data points within the range @@ -80,16 +84,16 @@ % figure out indices of all the points to be highlighted idx = xidx & yidx; -% return nothing if no points to plot -if ~any(idx), highlight = []; - -% create the highlight +if ~any(idx) + % return nothing if no points to plot + highlight = []; else - - highlight = line(xdata(idx),ydata(idx),... - 'Parent', gca,... - 'LineStyle', 'none',... - 'Marker', 'o',... - 'MarkerEdgeColor', 'white', ... - 'MarkerFaceColor', 'white'); + % create the highlight + highlight = line(xdata(idx),ydata(idx), ... + 'UserData', idx, ... + 'Parent', gca, ... + 'LineStyle', 'none', ... + 'Marker', 'o', ... + 'MarkerEdgeColor', 'white', ... + 'MarkerFaceColor', 'white'); end diff --git a/Graph/TimeSeries/highlightTimeSeriesTimeDepth.m b/Graph/TimeSeries/highlightTimeSeriesTimeDepth.m index 76279faa7..6a5bb78b8 100644 --- a/Graph/TimeSeries/highlightTimeSeriesTimeDepth.m +++ b/Graph/TimeSeries/highlightTimeSeriesTimeDepth.m @@ -81,15 +81,15 @@ end -if ~any(any(idx)), highlight = []; - +if ~any(any(idx)) + highlight = []; else - - highlight = line(Xdata(idx), Ydata(idx),... - 'Parent', gca,... - 'LineStyle', 'none',... - 'Marker', 'o',... - 'MarkerEdgeColor', 'white', ... - 'MarkerFaceColor', 'white',... - 'MarkerSize', 3); + highlight = line(Xdata(idx), Ydata(idx), ... + 'UserData', idx, ... + 'Parent', gca, ... + 'LineStyle', 'none', ... + 'Marker', 'o', ... + 'MarkerEdgeColor', 'white', ... + 'MarkerFaceColor', 'white', ... + 'MarkerSize', 3); end diff --git a/Graph/TimeSeries/highlightTimeSeriesTimeFrequencyDirection.m b/Graph/TimeSeries/highlightTimeSeriesTimeFrequencyDirection.m index 5ab1a3aad..2ec148767 100644 --- a/Graph/TimeSeries/highlightTimeSeriesTimeFrequencyDirection.m +++ b/Graph/TimeSeries/highlightTimeSeriesTimeFrequencyDirection.m @@ -77,15 +77,15 @@ end -if ~any(any(idx)), highlight = []; - +if ~any(any(idx)) + highlight = []; else - - highlight = line(Xdata(idx),Ydata(idx),... - 'Parent', gca,... - 'LineStyle', 'none',... - 'Marker', 'o',... - 'MarkerEdgeColor', 'white', ... - 'MarkerFaceColor', 'white',... - 'MarkerSize', 3); + highlight = line(Xdata(idx), Ydata(idx), ... + 'UserData', idx, ... + 'Parent', gca, ... + 'LineStyle', 'none', ... + 'Marker', 'o', ... + 'MarkerEdgeColor', 'white', ... + 'MarkerFaceColor', 'white', ... + 'MarkerSize', 3); end diff --git a/Graph/Transect/selectTransectGeneric.m b/Graph/Transect/selectTransectGeneric.m index 5bbc73276..cef7d0a87 100644 --- a/Graph/Transect/selectTransectGeneric.m +++ b/Graph/Transect/selectTransectGeneric.m @@ -2,7 +2,7 @@ function selectTransectGeneric( selectCallback, clickCallback ) %SELECTTRANSECTGENERIC Adds callbacks to the current figure, allowing the % user to interact with data in the current transect axis using the mouse. % -% See Graph/TimeSeries/selectTimeSeriesGeneric for more documentation. +% This function delegates to Graph/TimeSeries/selectTimeSeriesGeneric.m. % % Inputs: % selectCallback - function handle which is called when the user selects @@ -43,101 +43,4 @@ function selectTransectGeneric( selectCallback, clickCallback ) % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(2,2); - - if ~isa(selectCallback, 'function_handle') - error('selectCallback must be a function handle'); - end - if ~isa(clickCallback, 'function_handle') - error('clickCallback must be a function handle'); - end - - % get handle to the current figure - f = gcf; - - % add callbacks for dragging over an area of data/flags - - % this also accounts for click events - set(f, 'WindowButtonDownFcn', @buttonDown); - set(f, 'WindowButtonMotionFcn', @buttonMove); - set(f, 'WindowButtonUpFcn', @buttonUp); - - % state variables used during dragging - drag = false; - startPoint = []; - endPoint = []; - rect = []; - rectPos = []; - - function buttonDown(source,ev) - %BUTTONDOWN Captures the coordinates when the mouse is clicked on an - % axes. - % - % bail if the user clicks another mouse button while dragging - if drag - startPoint = []; - endPoint = []; - drag = false; - delete(rect); - rect = []; - return; - end - - startPoint = get(gca, 'CurrentPoint'); - startPoint = startPoint([1 3]); - - rectPos = [startPoint 0.0001 0.0001]; - - rect = rectangle(... - 'Parent', gca,... - 'Position', rectPos,... - 'EdgeColor', [1 1 1],... - 'LineStyle', '--',... - 'LineWidth', 0.25); - - drag = true; - end - - function buttonMove(source,ev) - %BUTTONMOVE Captures the coordinates when the mouse is dragged. Draws a - % rectangle from where the mouse was clicked to the current mouse location. - % - if ~drag, return; end - - endPoint = get(gca, 'CurrentPoint'); - endPoint = endPoint([1 3]); - - rectPos([1 2]) = min(startPoint, endPoint); - rectPos([3 4]) = abs(endPoint - startPoint); - - % guard against negative/zero width/height - if ~any(rectPos([3 4]) <= 0), set(rect, 'Position', rectPos); end - end - - function buttonUp(source,ev) - %BUTTONUP Captures coordinates when the mouse button is released. - % Calls the selectCallback. - % - if ~drag, return; end - - endPoint = get(gca, 'CurrentPoint'); - endPoint = endPoint([1 3]); - - range = [min(startPoint, endPoint), max(startPoint, endPoint)]; - - type = get(gcbf, 'SelectionType'); - - % click or drag? - click = false; - if startPoint == endPoint, click = true; end - - startPoint = []; - endPoint = []; - drag = false; - delete(rect); - rect = []; - - if click, clickCallback( gca, type, startPoint); - else selectCallback(gca, type, range); - end - end -end +selectTimeSeriesGeneric( selectCallback, clickCallback ); diff --git a/Graph/XvY/flagXvYGeneric.m b/Graph/XvY/flagXvYGeneric.m new file mode 100644 index 000000000..389e5ed1d --- /dev/null +++ b/Graph/XvY/flagXvYGeneric.m @@ -0,0 +1,112 @@ +function hFlags = flagXvYGeneric( ax, sample_data, var ) +%FLAGXVYGENERIC Draws overlays on the given XvY axis, to +% display QC flag data for the given variable. +% +% Draws a set of line objects on the given axis, to display the QC flags +% for the given variable. +% +% Inputs: +% ax - The axis on which to draw the QC data. +% sample_data - Struct containing sample data. +% var - Index into sample_data.variables, defining the variable +% in question. +% +% Outputs: +% flags - Vector of handles to line objects, which are the flag +% overlays. +% +% 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. +% +narginchk(3,3); + +if ~ishandle(ax), error('ax must be a graphics handle'); end +if ~isstruct(sample_data), error('sample_data must be a struct'); end +if ~isnumeric(var), error('var must be numeric'); end + +qcSet = str2double(readProperty('toolbox.qc_set')); +rawFlag = imosQCFlag('raw', qcSet, 'flag'); + +flags1 = sample_data.variables{var(1)}.flags; +flags2 = sample_data.variables{var(2)}.flags; +flags = max(flags1, flags2); +dataX = sample_data.variables{var(1)}.data; +dataY = sample_data.variables{var(2)}.data; + +% get a list of the different flag types to be graphed +flagTypes = unique(flags); + +% don't display raw data flags +iRawFlag = (flagTypes == rawFlag); +if any(iRawFlag), flagTypes(iRawFlag) = []; end + +lenFlag = length(flagTypes); + +% if no flags to plot, put a dummy handle in - the +% caller is responsible for checking and ignoring +hFlags = nan(lenFlag, 1); +if isempty(hFlags) + hFlags = 0.0; +end + +% a different line for each flag type +for m = 1:lenFlag + + f = (flags == flagTypes(m)); + + fc = imosQCFlag(flagTypes(m), qcSet, 'color'); + fn = strrep(imosQCFlag(flagTypes(m), qcSet, 'desc'), '_', ' '); + + fx = dataX(f); + fy = dataY(f); + + hFlags(m) = line(fx, fy,... + 'Parent', ax,... + 'LineStyle', 'none',... + 'Marker', 'o',... + 'MarkerFaceColor', fc,... + 'MarkerEdgeColor', 'none'); + + % Create a UICONTEXTMENU, and assign a UIMENU to it + hContext = uicontextmenu; + hMenu = uimenu('parent',hContext); + + % Set the UICONTEXTMENU to the line object + set(hFlags(m),'uicontextmenu',hContext); + + % Create a WindowButtonDownFcn callback that will update + % the label on the UICONTEXTMENU's UIMENU + % set(gcf,'WindowButtondownFcn', ... + % 'set(hMenu, ''label'', fn)'); + set(hMenu, 'label', fn); +end diff --git a/Graph/XvY/getSelectedXvYGeneric.m b/Graph/XvY/getSelectedXvYGeneric.m new file mode 100644 index 000000000..eb2b522f9 --- /dev/null +++ b/Graph/XvY/getSelectedXvYGeneric.m @@ -0,0 +1,52 @@ +function dataIdx = getSelectedXvYGeneric( sample_data, var, ax, highlight ) +%GETSELECTEDXVYGENERIC Returns the indices of the currently selected +% (highlighted) data on the given axis. +% +% This function is nearly identical to +% Graph/TimeSeries/getSelectedTimeSeriesGeneric.m. +% +% Inputs: +% sample_data - Struct containing the data set. +% var - Variable in question (index into sample_data.variables). +% ax - Axis in question. +% highlight - Handle to the highlight object. +% +% +% Outputs: +% dataIdx - Vector of indices into the data, defining the indices +% which are selected (and which were clicked on). +% +% 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. +% +dataIdx = getSelectedTimeSeriesGeneric(sample_data, var, ax, highlight); \ No newline at end of file diff --git a/Graph/XvY/graphXvYGeneric.m b/Graph/XvY/graphXvYGeneric.m new file mode 100644 index 000000000..23555aae3 --- /dev/null +++ b/Graph/XvY/graphXvYGeneric.m @@ -0,0 +1,79 @@ +function [h, labels] = graphXvYGeneric( ax, sample_data, vars ) +%GRAPHXVYGENERIC Plots the given variable (x axis) against another +% (y axis). +% +% Inputs: +% ax - Parent axis. +% sample_data - The data set. +% vars - The variables to plot. +% +% Outputs: +% h - Handle(s) to the line(s) which was/were plotted. +% labels - Cell array containing x/y labels to use. +% +% 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. +% +narginchk(3,3); + +if ~ishandle(ax), error('ax must be a graphics handle'); end +if ~isstruct(sample_data), error('sample_data must be a struct'); end +if ~isnumeric(vars), error('var must be a numeric'); end + +xdata = sample_data.variables{vars(1)}.data; +ydata = sample_data.variables{vars(2)}.data; + +h = line(xdata, ydata); +set(ax, 'Tag', 'axis1D'); + +% for global/regional range display +mWh = findobj('Tag', 'mainWindow'); +sMh = findobj('Tag', 'samplePopUpMenu'); +iSample = get(sMh, 'Value'); +climatologyRange = get(mWh, 'UserData'); +if ~isempty(climatologyRange) + if isfield(climatologyRange, ['rangeMin' sample_data.variables{vars(1)}.name]) + xLim = get(ax, 'XLim'); + line(climatologyRange(iSample).(['rangeMin' sample_data.variables{vars(1)}.name]), [sample_data.variables{vars(2)}.data(1); sample_data.variables{vars(2)}.data(end)], 'Parent', ax, 'Color', 'r'); + line(climatologyRange(iSample).(['rangeMax' sample_data.variables{vars(1)}.name]), [sample_data.variables{vars(2)}.data(1); sample_data.variables{vars(2)}.data(end)], 'Parent', ax, 'Color', 'r'); + set(ax, 'XLim', xLim); + end + if isfield(climatologyRange, ['rangeMin' sample_data.variables{vars(2)}.name]) + yLim = get(ax, 'YLim'); + line([sample_data.variables{vars(1)}.data(1); sample_data.variables{vars(1)}.data(end)], climatologyRange(iSample).(['rangeMin' sample_data.variables{vars(2)}.name]), 'Parent', ax, 'Color', 'r'); + line([sample_data.variables{vars(1)}.data(1); sample_data.variables{vars(1)}.data(end)], climatologyRange(iSample).(['rangeMax' sample_data.variables{vars(2)}.name]), 'Parent', ax, 'Color', 'r'); + set(ax, 'YLim', yLim); + end +end + +labels = {sample_data.variables{vars(1)}.name, sample_data.variables{vars(2)}.name}; \ No newline at end of file diff --git a/Graph/XvY/highlightXvYGeneric.m b/Graph/XvY/highlightXvYGeneric.m new file mode 100644 index 000000000..d67420511 --- /dev/null +++ b/Graph/XvY/highlightXvYGeneric.m @@ -0,0 +1,55 @@ +function highlight = highlightXvYGeneric( ... + region, data, variable, type ) +%HIGHLIGHTXVYGENERIC Highlights the given region on the given data +% axes, using a line overlaid on the points in the region. +% +% This function delegates to Graph/TimeSeries/highlightTimeSeriesGeneric.m. +% +% Inputs: +% region - a vector of length 4, containing the selected data region. +% Must be in the format: [lx ly hx hy] +% data - A handle, or vector of handles, to the graphics object(s) +% displaying the data (e.g. line, scatter). Must contain +% 'XData' and 'YData' properties. +% variable - The variable displayed on the axes. +% type - The highlight type. +% +% Outputs: +% highlight - Handle to a line object which overlays the highlighted +% data. If no data points lie within the highlight region, +% an empty matrix is returned. +% +% 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. +% +highlight = highlightTimeSeriesGeneric(region, data, variable, type); diff --git a/Graph/XvY/selectXvYGeneric.m b/Graph/XvY/selectXvYGeneric.m new file mode 100644 index 000000000..fa5a7a848 --- /dev/null +++ b/Graph/XvY/selectXvYGeneric.m @@ -0,0 +1,47 @@ +function selectXvYGeneric( selectCallback, clickCallback ) +%SELECTXVYGENERIC Adds callbacks to the current figure, allowing the +% user to interact with data in the current XvY axis using the mouse. +% +% This function delegates to Graph/TimeSeries/selectTimeSeriesGeneric.m. +% +% Inputs: +% selectCallback - function handle which is called when the user selects +% a region. +% +% clickCallback - function handle which is called when the user clicks +% on a point. +% +% 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. +% +selectTimeSeriesGeneric( selectCallback, clickCallback ); diff --git a/Graph/checkMooringPlannedDepths.m b/Graph/checkMooringPlannedDepths.m index f7e80d053..c19e58105 100644 --- a/Graph/checkMooringPlannedDepths.m +++ b/Graph/checkMooringPlannedDepths.m @@ -186,7 +186,7 @@ function checkMooringPlannedDepths(sample_data, isQC, saveToFile, exportDir) xMax = max(xMax); instrumentDesc = [{'Make Model (nominal depth - instrument SN)'}; instrumentDesc]; -hLineVar(1) = 0; +hLineVar(1) = line(0, 0, 'Visible', 'off', 'LineStyle', 'none', 'Marker', 'none'); %now plot all the calculated depths on one plot to choose region for comparison: %plot diff --git a/Graph/checkMooringPresDiffs.m b/Graph/checkMooringPresDiffs.m index 4f56e08bd..51f1c8290 100644 --- a/Graph/checkMooringPresDiffs.m +++ b/Graph/checkMooringPresDiffs.m @@ -65,7 +65,7 @@ function checkMooringPresDiffs(sample_data, iSampleMenu, isQC, saveToFile, expor stringQC = 'all'; if isQC, stringQC = 'only good and non QC''d'; end -title = [sample_data{1}.deployment_code ' mooring ' stringQC ' ' varTitle ' differences']; +title = [sample_data{1}.deployment_code ' mooring ' stringQC ' ' varTitle ' (' varUnit ') differences']; % retrieve good flag values qcSet = str2double(readProperty('toolbox.qc_set')); @@ -155,7 +155,7 @@ function checkMooringPresDiffs(sample_data, iSampleMenu, isQC, saveToFile, expor set(hAxPress, 'YDir', 'reverse') set(get(hAxPress, 'XLabel'), 'String', 'Time'); set(get(hAxPress, 'YLabel'), 'String', [presRelCode ' (' varUnit ')'], 'Interpreter', 'none'); -set(get(hAxPress, 'Title'), 'String', varTitle, 'Interpreter', 'none'); +set(get(hAxPress, 'Title'), 'String', [varTitle '( ' varUnit ')'], 'Interpreter', 'none'); set(hAxPress, 'XTick', (xMin:(xMax-xMin)/4:xMax)); set(hAxPress, 'XLim', [xMin, xMax]); hold(hAxPress, 'on'); @@ -163,9 +163,9 @@ function checkMooringPresDiffs(sample_data, iSampleMenu, isQC, saveToFile, expor %Pressure diff plot hAxPressDiff = subplot(2,1,2,'Parent', hPanelMooringVar); set(get(hAxPressDiff, 'XLabel'), 'String', 'Time'); -set(get(hAxPressDiff, 'YLabel'), 'String', [presRelCode ' (' varUnit ')'], 'Interpreter', 'none'); +set(get(hAxPressDiff, 'YLabel'), 'String', ['Pressure differences (' varUnit ')'], 'Interpreter', 'none'); set(get(hAxPressDiff, 'Title'), 'String', ... - ['Pressure Differences from ' instrumentDesc{iCurrSam} ' (in black above)'] , 'Interpreter', 'none'); + ['Pressure differences in ' varUnit ' (minus respective median over 1st quarter) between ' instrumentDesc{iCurrSam} ' (in black above) and nearest neighbours'] , 'Interpreter', 'none'); set(hAxPressDiff, 'XTick', (xMin:(xMax-xMin)/4:xMax)); set(hAxPressDiff, 'XLim', [xMin, xMax]); hold(hAxPressDiff, 'on'); @@ -215,12 +215,12 @@ function checkMooringPresDiffs(sample_data, iSampleMenu, isQC, saveToFile, expor % nearest (blue) to farthest (yellow) try defaultColormapFh = str2func(readProperty('visualQC.defaultColormap')); - cMap = colormap(hAxPress, defaultColormapFh(nOthers - 1)); + cMap = colormap(hAxPress, defaultColormapFh(nOthers)); catch e - cMap = colormap(hAxPress, parula(nOthers - 1)); + cMap = colormap(hAxPress, parula(nOthers)); end % current sample is black -cMap = [0, 0, 0; cMap]; +cMap(iOthers == iCurrSam, :) = [0, 0, 0]; %now add the other data: for i=1:nOthers @@ -259,18 +259,27 @@ function checkMooringPresDiffs(sample_data, iSampleMenu, isQC, saveToFile, expor %add pressure to the pressure plot hLineVar(iOthers(i)) = line(otherSamTime, ... otherSamPresRel, ... - 'Color', cMap(i, :), ... - 'LineStyle', '-','Parent',hAxPress); + 'Color', cMap(i, :), ... + 'LineStyle', '-', ... + 'Parent', hAxPress); %now put the data on the same timebase as the instrument of %interest - newdat = interp1(otherSamTime,otherSamPresRel,curSamTime); - pdiff = curSamPresRel - newdat; + otherSamPresRel = interp1(otherSamTime, otherSamPresRel, curSamTime); + + curSamTimeFirstQuarter = min(curSamTime) + (max(curSamTime) - min(curSamTime))/4; + iCurSamTimeFirstQuarter = curSamTime < curSamTimeFirstQuarter; + + otherSamPresRelMedianFirstQuarter = median(otherSamPresRel(~isnan(otherSamPresRel) & iCurSamTimeFirstQuarter)); + curSamPresRelMedianFirstQuarter = median(curSamPresRel(~isnan(curSamPresRel) & iCurSamTimeFirstQuarter)); + + pdiff = (curSamPresRel - curSamPresRelMedianFirstQuarter) - (otherSamPresRel - otherSamPresRelMedianFirstQuarter); hLineVar2(iOthers(i)) = line(curSamTime, ... pdiff, ... - 'Color', cMap(i, :), ... - 'LineStyle', '-','Parent',hAxPressDiff); + 'Color', cMap(i, :), ... + 'LineStyle', '-', ... + 'Parent', hAxPressDiff); % set background to be grey set(hAxPress, 'Color', backgroundColor) diff --git a/Graph/flagDepthProfile.m b/Graph/flagDepthProfile.m index 94c6d20e9..5ab233862 100644 --- a/Graph/flagDepthProfile.m +++ b/Graph/flagDepthProfile.m @@ -67,7 +67,6 @@ if depth == 0, error('data set contains no depth data'); end end -vars(vars == depth) = []; if isempty(vars), return; end hold on; diff --git a/Graph/flagXvY.m b/Graph/flagXvY.m new file mode 100644 index 000000000..02b68083e --- /dev/null +++ b/Graph/flagXvY.m @@ -0,0 +1,83 @@ +function flags = flagXvY( parent, graphs, sample_data, vars ) +%FLAGXY Overlays flags for the given sample data variables on the +% given XvY graphs. +% +% Inputs: +% parent - handle to parent figure/uipanel. +% graphs - vector handles to axis objects (one for each variable). +% sample_data - struct containing the sample data. +% vars - vector of indices into the sample_data.variables array. +% Must be the same length as graphs. +% +% Outputs: +% flags - handles to line objects that make up the flag overlays. +% +% 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. +% + +narginchk(4,4); + +if ~ishandle(parent), error('parent must be a graphic handle'); end +if ~ishandle(graphs), error('graphs must be a graphic handle(s)'); end +if ~isstruct(sample_data), error('sample_data must be a struct'); end +if ~isnumeric(vars), error('vars must be a numeric'); end + +flags = []; + +if isempty(vars), return; end +if length(vars) ~=2, return; end + +hold on; + +% apply the flag function for this combination of XvY variables +flagFunc = getGraphFunc('XvY', 'flag', sample_data.variables{vars(1)}.name); +f = flagFunc(graphs, sample_data, vars); + +% if the flag function returned nothing, insert a dummy handle +if isempty(f), f = 0.0; end + +% +% the following is some ugly code which takes the flag handle(s) returned +% from the variable-specific flag function, and saves it/them in the +% flags matrix, accounting for differences in size. +% + +fl = length(f); +fs = size(flags,2); + +if fl > fs, flags(:,fs+1:fl) = 0.0; +elseif fl < fs, f ( fl+1:fs) = 0.0; +end + +flags(1,:) = f; \ No newline at end of file diff --git a/Graph/graphDepthProfile.m b/Graph/graphDepthProfile.m index 7141c399c..7daf7d80e 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 ) %GRAPHDEPTHPROFILE Graphs the given data in a depth profile style using % subplots. % @@ -9,7 +9,7 @@ % 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. % % Outputs: % graphs - A vector of handles to axes on which the data has @@ -83,24 +83,8 @@ depth = getVar(sample_data.variables, 'DEPTH'); if depth ~= 0 - % we don't want to plot depth against itself, so if depth has been - % passed in as one of the variables to plot, remove it from the list - iAfterDepth = vars >= depth; - if any(iAfterDepth) - vars(iAfterDepth) = vars(iAfterDepth)+1; - iOutnumber = vars > length(sample_data.variables); - if any(iOutnumber) - vars(iOutnumber) = []; - if isempty(vars) - warning('no variables to graph'); - return; - end - end - end - depth = sample_data.variables{depth}; else - depth = getVar(sample_data.dimensions, 'DEPTH'); if depth == 0, error('data set contains no depth data'); end @@ -143,8 +127,8 @@ 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)); + 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 @@ -172,32 +156,42 @@ if sample_data.meta.level == 1 && strcmp(func2str(plotFunc), 'graphDepthProfileGeneric') qcSet = str2double(readProperty('toolbox.qc_set')); - - % set x and y limits so that axis are optimised for data below surface only + goodFlag = imosQCFlag('good', qcSet, 'flag'); + pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); + 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; - iGood = curDepth>=0; + iGood = (curFlag == goodFlag) | (curFlag == pGoodFlag) | (curFlag == rawFlag); - yLimits = [floor(min(curDepth(iGood))), ceil(max(curDepth(iGood)))]; - xLimits = [floor(min(curData(iGood))), ceil(max(curData(iGood)))]; + [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 = [floor(min(curDepth(iGood))*10)/10, ceil(max(curDepth(iGood))*10)/10]; + 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'))]; + 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 diff(xLimits) == 0; if xLimits(1) == 0 xLimits = [-1, 1]; else - eps=0.01*xLimits(1); - xLimits=[xLimits(1)-eps, xLimits(1)+eps]; + eps = 0.01 * xLimits(1); + xLimits = [xLimits(1) - eps, xLimits(1) + eps]; end end - if any(iGood) + if any(any(iGood)) set(graphs(k), 'YLim', yLimits); set(graphs(k), 'XLim', xLimits); end @@ -209,34 +203,4 @@ set(graphs(k), 'YTick', yTicks); end - - % GLT : Eventually I prefered not displaying the QC legend as it - % influences too badly the quality of the plots. I didn't manage to have - % a satisfying result with a ghost axis hosting the legend... So for now - % I added the possiblity to the user to right-click on a QC'd data point - % and it displays the description of the color flag. - % compile variable names for the legend -% names = {}; -% for k = 1:length(vars) -% -% names{k} = sample_data.variables{vars(k)}.name; -% end -% -% % link axes for panning/zooming, and add a legend - matlab has a habit of -% % throwing 'Invalid handle object' errors for no apparent reason (i think -% % when the user changes selections too quickly, matlab is too slow, and -% % ends up confusing itself), so absorb any errors which are thrown -% try -% linkaxes(graphs, 'y'); -% -% % When adding a single legend for multiple subplots, by default the legend -% % is added to the axis which corresponds to the first handle in the vector -% % that is passed in ('lines' in this case). This is a problem in our case, -% % because it means that the legend will be added to the left most axis, -% % whereas we want it to be added to the right-most axis. To get around -% % this, I'm reversing the order of the line handles (and names) before -% % passing them to the legend function. -% legend(flipud(lines(:,1)), fliplr(names)); -% catch e -% end end diff --git a/Graph/graphTimeSeries.m b/Graph/graphTimeSeries.m index 5b44b205b..689394f9b 100644 --- a/Graph/graphTimeSeries.m +++ b/Graph/graphTimeSeries.m @@ -53,10 +53,13 @@ % narginchk(3,3); - if ~ishandle( parent), error('parent must be a handle'); end - if ~isstruct( sample_data), error('sample_data must be a struct'); 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 + graphs = []; + lines = []; + if isempty(vars) return; end @@ -78,8 +81,13 @@ sample_data.variables = sample_data.variables(vars); lenVar = length(sample_data.variables); - graphs = nan(lenVar, 1); - lines = nan(lenVar, 1); + if verLessThan('matlab','8.1') %R2013a + graphs = nan(lenVar, 1); + lines = nan(lenVar, 1); + else + graphs = gobjects(lenVar, 1); + lines = gobjects(lenVar, 1); + end iTimeDim = getVar(sample_data.dimensions, 'TIME'); xLimits = [min(sample_data.dimensions{iTimeDim}.data), max(sample_data.dimensions{iTimeDim}.data)]; @@ -113,11 +121,13 @@ if sample_data.meta.level == 1 qcSet = str2double(readProperty('toolbox.qc_set')); - goodFlag = imosQCFlag('good', qcSet, 'flag'); - rawFlag = imosQCFlag('raw', qcSet, 'flag'); + goodFlag = imosQCFlag('good', qcSet, 'flag'); + pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); + rawFlag = imosQCFlag('raw', qcSet, 'flag'); - % set x and y limits so that axis are optimised for good/raw data only + % 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); if any(iGood) varData = sample_data.variables{k}.data(iGood); @@ -125,8 +135,7 @@ minData = min(varData); maxData = max(varData); - yLimits = [floor(minData*10)/10, ... - ceil(maxData*10)/10]; + yLimits = [floor(minData*10)/10, ceil(maxData*10)/10]; end end case 'graphTimeSeriesTimeDepth' @@ -177,7 +186,8 @@ % multiple axes, this is not done automatically for us col = get(graphs(k), 'ColorOrder'); - % we get rid of the red color + % 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; diff --git a/Graph/graphXvY.m b/Graph/graphXvY.m index 018f80482..f58023cd8 100644 --- a/Graph/graphXvY.m +++ b/Graph/graphXvY.m @@ -1,21 +1,20 @@ -function [graphs lines vars] = graphXvY( parent, sample_data, vars ) -%GRAPHTRANSECT Graphs the first two variables from the given data set +function [graphs, lines, vars] = graphXvY( parent, sample_data, vars ) +%GRAPHTRANSECT Graphs the two variables selected from the given data set % against each other on an X-Y axis. % % 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. % % Outputs: -% graphs - A vector of handles to axes on which the data has +% graphs - A vector of handles to the axes on which the data has % been graphed. -% lines - A matrix of handles to line or surface (or other) -% handles which have been drawn, the same length as -% graphs. +% lines - A matrix of handles to lines which has been drawn. % vars - Indices of variables which were graphed. % -% Author: Paul McCarthy +% Author: Paul McCarthy +% Contributor: Guillaume Galibert % % @@ -56,48 +55,103 @@ graphs = []; lines = []; - if length(vars) < 2 - warning('not enough variables to graph'); + if length(vars) ~= 2 + warning('2 variables and only 2 need to be selected to graph'); return; end - vars = vars(1:2); + % 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 + p = getVar(sample_data.variables, 'BOT_DEPTH'); + case 'timeSeries' + % we don't want to plot TIMESERIES, PROFILE, TRAJECTORY, LATITUDE, LONGITUDE, NOMINAL_DEPTH + p = getVar(sample_data.variables, 'NOMINAL_DEPTH'); + 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 - for k = 1:length(vars) - - % m points to the other variable - m = k; - if m == 1, m = 2; - else m = 1; - end - - xname = sample_data.variables{vars(k)}.name; - yname = sample_data.variables{vars(m)}.name; - - xdata = sample_data.variables{vars(k)}.data; - ydata = sample_data.variables{vars(m)}.data; - - % create the axes - graphs(k) = subplot(1, length(vars), k); - - set(graphs(k), 'Parent', parent,... - 'XGrid', 'on',... - 'Color', 'none',... - 'YGrid', 'on', ... - 'ZGrid', 'on'); - - lines(k) = line(xdata, ydata); - - % set labels - set(get(graphs(k), 'XLabel'), 'String', xname, 'Interpreter', 'none'); - set(get(graphs(k), 'YLabel'), 'String', yname, 'Interpreter', 'none'); + xname = sample_data.variables{vars(1)}.name; + yname = sample_data.variables{vars(2)}.name; + + % create the axes + graphs = axes('Parent', parent,... + 'XGrid', 'on',... + 'Color', 'none',... + 'YGrid', 'on', ... + 'ZGrid', 'on'); + + % plot the variable + plotFunc = getGraphFunc('XvY', 'graph', xname); + [lines, labels] = plotFunc(graphs, sample_data, vars); + + set(lines, 'Color', 'blue'); + + % set x label + uom = ''; + try uom = [' (' imosParameters(labels{1}, 'uom') ')']; + catch e, uom = ''; end + xLabel = [labels{1} uom]; + set(get(graphs, 'XLabel'), 'String', xLabel, 'Interpreter', 'none'); - set(lines(1), 'Color', 'blue'); - set(lines(2), 'Color', 'red'); + % set y label for the first plot + try uom = [' (' imosParameters(labels{2}, 'uom') ')']; + catch e, uom = ''; + end + yLabel = [labels{2} uom]; + if length(yLabel) > 20, yLabel = [yLabel(1:17) '...']; end + set(get(graphs, 'YLabel'), 'String', yLabel, 'Interpreter', 'none'); + + if sample_data.meta.level == 1 && strcmp(func2str(plotFunc), 'graphXvYGeneric') + qcSet = str2double(readProperty('toolbox.qc_set')); + goodFlag = imosQCFlag('good', qcSet, 'flag'); + pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); + rawFlag = imosQCFlag('raw', qcSet, 'flag'); + + % set x and y limits so that axis are optimised for good/probably good/raw data only + curDataX = sample_data.variables{vars(1)}.data; + curDataY = sample_data.variables{vars(2)}.data; + curFlagX = sample_data.variables{vars(1)}.flags; + curFlagY = sample_data.variables{vars(2)}.flags; + + curFlag = max(curFlagX, curFlagY); + iGood = (curFlag == goodFlag) | (curFlag == pGoodFlag) | (curFlag == rawFlag); + + yLimits = [floor(min(curDataY(iGood))*10)/10, ceil(max(curDataY(iGood))*10)/10]; + xLimits = [floor(min(curDataX(iGood))*10)/10, ceil(max(curDataX(iGood))*10)/10]; + + %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 + + %check for yLimits max=min + if diff(yLimits)==0; + if yLimits(1) == 0 + yLimits = [-1, 1]; + else + eps=0.01*yLimits(1); + yLimits=[yLimits(1)-eps, yLimits(1)+eps]; + end + end + + if any(iGood) + set(graphs, 'YLim', yLimits); + set(graphs, 'XLim', xLimits); + end + end + end diff --git a/Graph/lineCastVar.m b/Graph/lineCastVar.m index fae12ed2a..8e926998a 100644 --- a/Graph/lineCastVar.m +++ b/Graph/lineCastVar.m @@ -98,7 +98,7 @@ function lineCastVar(sample_data, varNames, isQC, saveToFile, exportDir) hLineFlag = ones(4, 1); instrumentDesc{1} = 'Make Model (instrument SN - cast time)'; - hLineVar(1) = 0; + hLineVar(1) = line(0, 0, 'Visible', 'off', 'LineStyle', 'none', 'Marker', 'none'); for i=1:lenSampleData % instrument description diff --git a/Graph/lineMooring1DVar.m b/Graph/lineMooring1DVar.m index c1d1f7f62..00af4de4c 100644 --- a/Graph/lineMooring1DVar.m +++ b/Graph/lineMooring1DVar.m @@ -57,8 +57,16 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) monitorRect = getRectMonitor(); iBigMonitor = getBiggestMonitor(); -varTitle = imosParameters(varName, 'long_name'); -varUnit = imosParameters(varName, 'uom'); +if strcmp(varName, 'diff(TIME)') + varName = 'TIME'; + typeVar = 'dimensions'; + varTitle = ['diff ' imosParameters(varName, 'long_name')]; + varUnit = 's'; +else + typeVar = 'variables'; + varTitle = imosParameters(varName, 'long_name'); + varUnit = imosParameters(varName, 'uom'); +end stringQC = 'all'; if isQC, stringQC = 'only good and non QC''d'; end @@ -87,14 +95,14 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) end iTime = getVar(sample_data{i}.dimensions, 'TIME'); - iVar = getVar(sample_data{i}.variables, varName); + iVar = getVar(sample_data{i}.(typeVar), varName); iGood = true(size(sample_data{i}.dimensions{iTime}.data)); % the variable exists, is QC'd and is 1D - if isQC && iVar && size(sample_data{i}.variables{iVar}.data, 2) == 1 + if isQC && iVar && size(sample_data{i}.(typeVar){iVar}.data, 2) == 1 %get time and var QC information timeFlags = sample_data{i}.dimensions{iTime}.flags; - varFlags = sample_data{i}.variables{iVar}.flags; + varFlags = sample_data{i}.(typeVar){iVar}.flags; iGood = ismember(timeFlags, goodFlags) & ismember(varFlags, goodFlags); end @@ -115,7 +123,7 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) hLineVar = nan(lenSampleData + 1, 1); instrumentDesc{1} = 'Make Model (nominal depth - instrument SN)'; -hLineVar(1) = 0; +hLineVar(1) = line(0, 0, 'Visible', 'off', 'LineStyle', 'none', 'Marker', 'none'); initiateFigure = true; isPlottable = false; @@ -139,10 +147,10 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) %look for time and relevant variable iTime = getVar(sample_data{iSort(i)}.dimensions, 'TIME'); - iVar = getVar(sample_data{iSort(i)}.variables, varName); + iVar = getVar(sample_data{iSort(i)}.(typeVar), varName); - if iVar > 0 && size(sample_data{iSort(i)}.variables{iVar}.data, 2) == 1 && ... % we're only plotting 1D variables but no current - all(~strncmpi(sample_data{iSort(i)}.variables{iVar}.name, {'UCUR', 'VCUR', 'WCUR', 'CDIR', 'CSPD', 'VEL1', 'VEL2', 'VEL3'}, 4)) + if iVar > 0 && size(sample_data{iSort(i)}.(typeVar){iVar}.data, 2) == 1 && ... % we're only plotting 1D variables but no current + all(~strncmpi(sample_data{iSort(i)}.(typeVar){iVar}.name, {'UCUR', 'VCUR', 'WCUR', 'CDIR', 'CSPD', 'VEL1', 'VEL2', 'VEL3'}, 4)) if initiateFigure fileName = genIMOSFileName(sample_data{iSort(i)}, 'png'); hFigMooringVar = figure(... @@ -158,7 +166,9 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) if any(strcmpi(varName, {'DEPTH', 'PRES', 'PRES_REL'})), set(hAxMooringVar, 'YDir', 'reverse'); end set(get(hAxMooringVar, 'XLabel'), 'String', 'Time'); - set(get(hAxMooringVar, 'YLabel'), 'String', [varName ' (' varUnit ')'], 'Interpreter', 'none'); + yLabel = [varName ' (' varUnit ')']; + if strcmpi(varName, 'TIME'), yLabel = ['diff ' yLabel]; end + set(get(hAxMooringVar, 'YLabel'), 'String', yLabel, 'Interpreter', 'none'); set(get(hAxMooringVar, 'Title'), 'String', title, 'Interpreter', 'none'); set(hAxMooringVar, 'XTick', (xMin:(xMax-xMin)/4:xMax)); set(hAxMooringVar, 'XLim', [xMin, xMax]); @@ -197,12 +207,12 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) set(get(get(hNominalDepth,'Annotation'),'LegendInformation'),'IconDisplayStyle','off'); end - iGood = true(size(sample_data{iSort(i)}.variables{iVar}.data)); + iGood = true(size(sample_data{iSort(i)}.(typeVar){iVar}.data)); if isQC %get time and var QC information timeFlags = sample_data{iSort(i)}.dimensions{iTime}.flags; - varFlags = sample_data{iSort(i)}.variables{iVar}.flags; + varFlags = sample_data{iSort(i)}.(typeVar){iVar}.flags; iGood = ismember(timeFlags, goodFlags) & ismember(varFlags, goodFlags); end @@ -217,17 +227,26 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) xLine = sample_data{iSort(i)}.dimensions{iTime}.data; xLine(~iGood) = NaN; - dataVar = sample_data{iSort(i)}.variables{iVar}.data; + dataVar = sample_data{iSort(i)}.(typeVar){iVar}.data; dataVar(~iGood) = NaN; - hLineVar(i + 1) = line(xLine, ... - dataVar, ... + if strcmpi(varName, 'TIME') + xLine(1) = []; + dataVar = diff(dataVar)*24*3600; + end + + hLineVar(i + 1) = line(xLine, dataVar, ... 'Color', cMap(i, :)); userData.idx = iSort(i); userData.xName = 'TIME'; - userData.yName = varName; + if strcmpi(varName, 'TIME') + userData.yName = 'diff(TIME)'; + else + userData.yName = varName; + end set(hLineVar(i + 1), 'UserData', userData); clear('userData'); + % Let's redefine properties after pcolor to make sure grid lines appear % above color data and XTick and XTickLabel haven't changed set(hAxMooringVar, ... @@ -243,6 +262,41 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) end if ~initiateFigure && isPlottable + if ~isQC + % we add the in/out water boundaries + % for global/regional range and in/out water display + mWh = findobj('Tag', 'mainWindow'); + qcParam = get(mWh, 'UserData'); + yLim = get(hAxMooringVar, 'YLim'); + for i=1:lenSampleData + if isfield(qcParam, 'inWater') + line([qcParam(iSort(i)).inWater, qcParam(iSort(i)).inWater, NaN, qcParam(iSort(i)).outWater, qcParam(iSort(i)).outWater], ... + [yLim, NaN, yLim], ... + 'Parent', hAxMooringVar, ... + 'Color', 'r', ... + 'LineStyle', '--'); + + iTime = getVar(sample_data{i}.dimensions, 'TIME'); + xLine = sample_data{iSort(i)}.dimensions{iTime}.data; + + iVar = getVar(sample_data{iSort(i)}.(typeVar), varName); + dataVar = sample_data{iSort(i)}.(typeVar){iVar}.data; + + if strcmpi(varName, 'TIME') + xLine(1) = []; + dataVar = diff(dataVar)*24*3600; + end + + text(qcParam(iSort(i)).inWater, double(dataVar(find(xLine >= qcParam(iSort(i)).inWater, 1, 'first'))), ... + ['inWater @ ' datestr(qcParam(iSort(i)).inWater, 'yyyy-mm-dd HH:MM:SS UTC') ' - ' instrumentDesc{i + 1}], ... + 'Parent', hAxMooringVar); + text(qcParam(iSort(i)).outWater, double(dataVar(find(xLine <= qcParam(iSort(i)).outWater, 1, 'last'))), ... + ['outWater @ ' datestr(qcParam(iSort(i)).outWater, 'yyyy-mm-dd HH:MM:SS UTC') ' - ' instrumentDesc{i + 1}], ... + 'Parent', hAxMooringVar); + end + end + end + iNan = isnan(hLineVar); if any(iNan) hLineVar(iNan) = []; @@ -320,6 +374,8 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) xName = userData.xName; yName = userData.yName; + if strcmp(yName, 'diff(TIME)'), yName = 'TIME'; end + sam = sample_data{userData.idx}; ixVar = getVar(sam.dimensions, xName); @@ -346,15 +402,17 @@ function lineMooring1DVar(sample_data, varName, isQC, saveToFile, exportDir) xStr = [num2str(posClic(1)) ' ' xUnits]; end - if strcmp(yName, 'TIME') + if strcmp(userData.yName, 'TIME') yStr = datestr(posClic(2),'dd-mm-yyyy HH:MM:SS.FFF'); + elseif strcmp(userData.yName, 'diff(TIME)') + yStr = [num2str(posClic(2)) ' (s)']; %num2str(posClic(2),4) else yStr = [num2str(posClic(2)) ' (' yUnits ')']; %num2str(posClic(2),4) end datacursorText = {get(p,'DisplayName'),... [xName ': ' xStr],... - [yName ': ' yStr]}; + [userData.yName ': ' yStr]}; %datacursorText{end+1} = ['FileName: ',get(p,'Tag')]; end diff --git a/Graph/lineMooring2DVarSection.m b/Graph/lineMooring2DVarSection.m index 5f65bd3af..65150eacc 100644 --- a/Graph/lineMooring2DVarSection.m +++ b/Graph/lineMooring2DVarSection.m @@ -60,35 +60,29 @@ function lineMooring2DVarSection(sample_data, varName, timeValue, isQC, saveToFi monitorRect = getRectMonitor(); iBigMonitor = getBiggestMonitor(); -iVar = getVar(sample_data.variables, varName); -title = [sample_data.variables{iVar}.name ' section of ' sample_data.deployment_code ' at ' datestr(timeValue, 'yyyy-mm-dd HH:MM:SS UTC')]; - initiateFigure = true; varTitle = imosParameters(varName, 'long_name'); -varUnit = imosParameters(varName, 'uom'); - -if ~isempty(sample_data.meta.depth) - metaDepth = sample_data.meta.depth; -elseif ~isempty(sample_data.instrument_nominal_depth) - metaDepth = sample_data.instrument_nominal_depth; -else - metaDepth = NaN; -end +varUnit = imosParameters(varName, 'uom'); -instrumentDesc = cell(2, 1); -hLineVar = nan(2, 1); +varDesc = cell(3, 1); +hLineVar = nan(3, 1); flagDesc = cell(4, 1); hLineFlag = ones(4, 1); -instrumentDesc{1} = 'Make Model (nominal_depth - instrument SN)'; -hLineVar(1) = 0; - % instrument description if ~isempty(strtrim(sample_data.instrument)) - instrumentDesc{2} = sample_data.instrument; + instrumentDesc = sample_data.instrument; elseif ~isempty(sample_data.toolbox_input_file) - [~, instrumentDesc{2}] = fileparts(sample_data.toolbox_input_file); + [~, instrumentDesc] = fileparts(sample_data.toolbox_input_file); +end + +if ~isempty(sample_data.meta.depth) + metaDepth = sample_data.meta.depth; +elseif ~isempty(sample_data.instrument_nominal_depth) + metaDepth = sample_data.instrument_nominal_depth; +else + metaDepth = NaN; end instrumentSN = ''; @@ -96,7 +90,9 @@ function lineMooring2DVarSection(sample_data, varName, timeValue, isQC, saveToFi instrumentSN = sample_data.instrument_serial_number; end -instrumentDesc{2} = [strrep(instrumentDesc{2}, '_', ' ') ' (' num2str(metaDepth) 'm - ' instrumentSN ')']; +instrumentDesc = [strrep(instrumentDesc, '_', ' ') ' (' num2str(metaDepth) 'm - ' instrumentSN ')']; + +title = [varName ' section of ' instrumentDesc ' from ' sample_data.deployment_code ' @ ' datestr(timeValue, 'yyyy-mm-dd HH:MM:SS UTC')]; %look for 2nd dimension and relevant variable iVar = getVar(sample_data.variables, varName); @@ -110,7 +106,7 @@ function lineMooring2DVarSection(sample_data, varName, timeValue, isQC, saveToFi dimName = sample_data.dimensions{i2Ddim}.name; dimTitle = imosParameters(dimName, 'long_name'); -dimUnit = imosParameters(dimName, 'uom'); +dimUnit = imosParameters(dimName, 'uom'); backgroundColor = [0.75 0.75 0.75]; @@ -137,21 +133,20 @@ function lineMooring2DVarSection(sample_data, varName, timeValue, isQC, saveToFi hold(hAxVarSection, 'on'); - % dummy entry for first entry in legend - hLineVar(1) = plot(0, 0, 'o', 'color', backgroundColor, 'Visible', 'off'); % color grey same as background (invisible) - yLine = sample_data.dimensions{i2Ddim}.data; - dataVar = sample_data.variables{iVar}.data(iX); + dataVar = sample_data.variables{iVar}.data; + dataVarProfile = dataVar(iX); - hLineVar(2) = line(dataVar, ... + % plot data profile + hLineVar(1) = line(dataVarProfile, ... yLine, ... 'LineStyle', '-'); - xLim = get(hAxVarSection, 'XLim'); - yLim = get(hAxVarSection, 'YLim'); + varDesc{1} = [varName ' @ ' datestr(timeValue, 'yyyy-mm-dd HH:MM:SS UTC')]; - %get var QC information - varFlags = sample_data.variables{iVar}.flags(iX); + % get var QC information + flagsVar = sample_data.variables{iVar}.flags; + flagsVarProfile = flagsVar(iX); qcSet = str2double(readProperty('toolbox.qc_set')); @@ -160,82 +155,68 @@ function lineMooring2DVarSection(sample_data, varName, timeValue, isQC, saveToFi flagPBad = imosQCFlag('probablyBad', qcSet, 'flag'); flagBad = imosQCFlag('bad', qcSet, 'flag'); - iGood = varFlags == flagGood; - iProbablyGood = varFlags == flagPGood; - iProbablyBad = varFlags == flagPBad; - iBad = varFlags == flagBad; + iBad = flagsVar == flagBad; + iPBad = flagsVar == flagPBad; + + iGoodProfile = flagsVarProfile == flagGood; + iPGoodProfile = flagsVarProfile == flagPGood; + iPBadProfile = flagsVarProfile == flagPBad; + iBadProfile = flagsVarProfile == flagBad; - if all(~iGood & ~iProbablyGood) && isQC + if all(iPBadProfile | iBadProfile) && isQC fprintf('%s\n', ['Warning : in ' sample_data.toolbox_input_file ... ', there is not any ' varName ' data with good flags.']); end - flagDesc{1} = imosQCFlag(flagGood, qcSet, 'desc'); - fc = imosQCFlag(flagGood, qcSet, 'color'); - hLineFlag(1) = line(NaN, NaN, ... - 'LineStyle', 'none', ... - 'Marker', 'o', ... - 'MarkerFaceColor', fc, ... - 'MarkerEdgeColor', 'none', ... - 'Visible', 'off'); % this is to make sure all flags are properly displayed within legend - if any(iGood) - hLineFlag(1) = line(dataVar(iGood), ... - yLine(iGood), ... - 'LineStyle', 'none', ... - 'Marker', 'o', ... - 'MarkerFaceColor', fc, ... - 'MarkerEdgeColor', 'none'); + iMean = ~iBad & ~iPBad; + nRows = size(dataVar, 2); + dataVarMean = NaN(nRows, 1); + dataVarStd = NaN(nRows, 1); + for j=1:nRows + dataVarMean(j) = mean(dataVar(iMean(:, j), j)); + dataVarStd (j) = std (dataVar(iMean(:, j), j)); end - flagDesc{2} = imosQCFlag(flagPGood, qcSet, 'desc'); - fc = imosQCFlag(flagPGood, qcSet, 'color'); - hLineFlag(2) = line(NaN, NaN, ... - 'LineStyle', 'none', ... - 'Marker', 'o', ... - 'MarkerFaceColor', fc, ... - 'MarkerEdgeColor', 'none', ... - 'Visible', 'off'); - if any(iProbablyGood) - hLineFlag(2) = line(dataVar(iProbablyGood), ... - yLine(iProbablyGood), ... - 'LineStyle', 'none', ... - 'Marker', 'o', ... - 'MarkerFaceColor', fc, ... - 'MarkerEdgeColor', 'none'); - end + hLineVar(2) = line(dataVarMean, ... + yLine, ... + 'LineStyle', '--', ... + 'Color', 'g'); - flagDesc{3} = imosQCFlag(flagPBad, qcSet, 'desc'); - fc = imosQCFlag(flagPBad, qcSet, 'color'); - hLineFlag(3) = line(NaN, NaN, ... - 'LineStyle', 'none', ... - 'Marker', 'o', ... - 'MarkerFaceColor', fc, ... - 'MarkerEdgeColor', 'none', ... - 'Visible', 'off'); - if any(iProbablyBad) - hLineFlag(3) = line(dataVar(iProbablyBad), ... - yLine(iProbablyBad), ... - 'LineStyle', 'none', ... - 'Marker', 'o', ... - 'MarkerFaceColor', fc, ... - 'MarkerEdgeColor', 'none'); - end + varDesc{2} = [varName ' mean']; + + hLineVar(3) = line(dataVarMean + 3*dataVarStd, ... + yLine, ... + 'LineStyle', '--', ... + 'Color', 'r'); + + line(dataVarMean - 3*dataVarStd, ... + yLine, ... + 'LineStyle', '--', ... + 'Color', 'r'); + + varDesc{3} = [varName ' mean +/- 3*standard deviation']; + + flags = [flagGood, flagPGood, flagPBad, flagBad]; + iFlagsProfile = [iGoodProfile, iPGoodProfile, iPBadProfile, iBadProfile]; - flagDesc{4} = imosQCFlag(flagBad, qcSet, 'desc'); - fc = imosQCFlag(flagBad, qcSet, 'color'); - hLineFlag(4) = line(NaN, NaN, ... - 'LineStyle', 'none', ... - 'Marker', 'o', ... - 'MarkerFaceColor', fc, ... - 'MarkerEdgeColor', 'none', ... - 'Visible', 'off'); - if any(iBad) - hLineFlag(4) = line(dataVar(iBad), ... - yLine(iBad), ... + % plot flags on top of data profile + for i=1:4 + flagDesc{i} = imosQCFlag(flags(i), qcSet, 'desc'); + fc = imosQCFlag(flags(i), qcSet, 'color'); + hLineFlag(i) = line(NaN, NaN, ... 'LineStyle', 'none', ... 'Marker', 'o', ... 'MarkerFaceColor', fc, ... - 'MarkerEdgeColor', 'none'); + 'MarkerEdgeColor', 'none', ... + 'Visible', 'off'); % this is to make sure all flags are properly displayed within legend + if any(iFlagsProfile(:, i)) + hLineFlag(i) = line(dataVarProfile(iFlagsProfile(:, i)), ... + yLine(iFlagsProfile(:, i)), ... + 'LineStyle', 'none', ... + 'Marker', 'o', ... + 'MarkerFaceColor', fc, ... + 'MarkerEdgeColor', 'none'); + end end % Let's redefine properties after line to make sure grid lines appear @@ -253,18 +234,17 @@ function lineMooring2DVarSection(sample_data, varName, timeValue, isQC, saveToFi iNan = isnan(hLineVar); if any(iNan) hLineVar(iNan) = []; - instrumentDesc(iNan) = []; + varDesc(iNan) = []; end hLineVar = [hLineVar; hLineFlag]; - instrumentDesc = [instrumentDesc; flagDesc]; + varDesc = [varDesc; flagDesc]; % Matlab >R2015 legend entries for data which are not plotted % will be shown with reduced opacity - hLegend = legend(hAxVarSection, ... - hLineVar, regexprep(instrumentDesc,'_','\_'), ... + legend(hAxVarSection, ... + hLineVar, regexprep(varDesc,'_','\_'), ... 'Interpreter', 'none', ... 'Location', 'SouthOutside'); - % set(hLegend, 'Box', 'off', 'Color', 'none'); end if saveToFile diff --git a/Graph/scatterMooring1DVarAgainstDepth.m b/Graph/scatterMooring1DVarAgainstDepth.m index 48a51a697..e17cfe585 100644 --- a/Graph/scatterMooring1DVarAgainstDepth.m +++ b/Graph/scatterMooring1DVarAgainstDepth.m @@ -119,7 +119,7 @@ function scatterMooring1DVarAgainstDepth(sample_data, varName, isQC, saveToFile, hScatterVar = nan(lenSampleData + 1, 1); instrumentDesc{1} = 'Make Model (nominal depth - instrument SN)'; -hScatterVar(1) = 0; +hScatterVar(1) = line(0, 0, 'Visible', 'off', 'LineStyle', 'none', 'Marker', 'none'); % we need to go through every instruments to figure out the CLim properties % on which the subset plots happen below. diff --git a/Graph/scatterMooring2DVarAgainstDepth.m b/Graph/scatterMooring2DVarAgainstDepth.m index 5f91d3f48..22e2eb2ad 100644 --- a/Graph/scatterMooring2DVarAgainstDepth.m +++ b/Graph/scatterMooring2DVarAgainstDepth.m @@ -131,7 +131,7 @@ function scatterMooring2DVarAgainstDepth(sample_data, varName, isQC, saveToFile, hScatterVar = nan(lenSampleData + 1, 1); instrumentDesc{1} = 'Make Model (nominal depth - instrument SN)'; -hScatterVar(1) = 0; +hScatterVar(1) = line(0, 0, 'Visible', 'off', 'LineStyle', 'none', 'Marker', 'none'); % we need to go through every instruments to figure out the CLim properties % on which the subset plots happen below. @@ -204,7 +204,7 @@ function scatterMooring2DVarAgainstDepth(sample_data, varName, isQC, saveToFile, case {'UCUR', 'VCUR', 'WCUR', 'ECUR', 'VEL1', 'VEL2', 'VEL3'} % 0 centred parameters cMap = 'r_b'; cType = 'centeredOnZero'; - CLim = [-yLimMax yLimMax]; + CLim = [-max(abs(yLimMin), abs(yLimMax)) max(abs(yLimMax), abs(yLimMin))]; case {'CDIR', 'SSWD'} % directions [0; 360[ cMap = 'rkbwr'; cType = 'direction'; diff --git a/Java/ddb.jar b/Java/ddb.jar index 397eeff22..435f27339 100644 Binary files a/Java/ddb.jar and b/Java/ddb.jar differ diff --git a/Java/src/org/imos/ddb/DDB.java b/Java/src/org/imos/ddb/DDB.java index 789dd899f..c460f98e9 100644 --- a/Java/src/org/imos/ddb/DDB.java +++ b/Java/src/org/imos/ddb/DDB.java @@ -81,7 +81,6 @@ public static DDB getDDB(String name) throws Exception { return new ODBCDDB(name); else { System.err.println(name + " is not a valid filepath!"); - System.exit(1); } } diff --git a/Parser/DR1050Parse.m b/Parser/DR1050Parse.m index b6b4206e3..d0fc7ede9 100644 --- a/Parser/DR1050Parse.m +++ b/Parser/DR1050Parse.m @@ -144,7 +144,7 @@ line = fgetl(fid); % a single blank line separates the header from the data - while ~isempty(line) + while ~isempty(line) && ischar(line) lines = [lines line]; line = fgetl(fid); diff --git a/Parser/SBE19Parse.m b/Parser/SBE19Parse.m index 319e1b4d2..249d14f78 100644 --- a/Parser/SBE19Parse.m +++ b/Parser/SBE19Parse.m @@ -437,8 +437,10 @@ voltCalExpr = 'volt (\d): offset = (\S+), slope = (\S+)'; otherExpr = '^\*\s*([^\s=]+)\s*=\s*([^\s=]+)\s*$'; firmExpr = '(\S+)'; +firmExpr2 = '^\*\s*FirmwareVersion:\s*(\S+)'; %SBE39plus sensorId = ''; sensorType = '<[tT]ype>(.*\S+.*)'; +serialExpr = '^\*\s*SerialNumber:\s*(\S+)'; %SBE39plus exprs = {... headerExpr headerExpr2 headerExpr3 scanExpr ... @@ -448,7 +450,7 @@ castExpr castExpr2 intervalExpr ... sbe38Expr optodeExpr ... voltCalExpr otherExpr ... - firmExpr sensorId sensorType}; + firmExpr sensorId sensorType firmExpr2 serialExpr}; for k = 1:length(headerLines) @@ -590,6 +592,15 @@ header.sensorTypes = {}; end header.sensorTypes{end+1} = tkns{1}{1}; + + %FirmwareVersion, SBE39plus cnv + case 24 + header.instrument_firmware = tkns{1}{1}; + + % SerialNumber, SBE39plus cnv + case 25 + header.instrument_serial_no = tkns{1}{1}; + end break; end diff --git a/Parser/SBE3x.m b/Parser/SBE3x.m index df60440b2..bcc247159 100644 --- a/Parser/SBE3x.m +++ b/Parser/SBE3x.m @@ -214,7 +214,7 @@ % name = value % line = fgetl(fid); -while isempty(line) || line(1) == '*' || line(1) == 's' +while (isempty(line) || line(1) == '*' || line(1) == 's') && ischar(line) if isempty(line) || line(1) == 's' line = fgetl(fid); diff --git a/Parser/aquadoppProfilerParse.m b/Parser/aquadoppProfilerParse.m index 946f23646..f5659a2dd 100644 --- a/Parser/aquadoppProfilerParse.m +++ b/Parser/aquadoppProfilerParse.m @@ -215,10 +215,32 @@ height = -height; distance = -distance; end -iWellOriented = adcpOrientations == adcpOrientation; % we'll only keep data collected when ADCP is oriented as expected +iBadOriented = adcpOrientations ~= adcpOrientation; % we'll only keep velocity data collected when ADCP is oriented as expected +velocity2(iBadOriented, :) = NaN; +velocity1(iBadOriented, :) = NaN; +velocity3(iBadOriented, :) = NaN; +backscatter1(iBadOriented, :) = NaN; +backscatter2(iBadOriented, :) = NaN; +backscatter3(iBadOriented, :) = NaN; +if velocityProcessed + sig2noise1(iBadOriented, :) = NaN; + sig2noise2(iBadOriented, :) = NaN; + sig2noise3(iBadOriented, :) = NaN; + stdDev1(iBadOriented, :) = NaN; + stdDev2(iBadOriented, :) = NaN; + stdDev3(iBadOriented, :) = NaN; + errorCode1(iBadOriented, :) = NaN; + errorCode2(iBadOriented, :) = NaN; + errorCode3(iBadOriented, :) = NaN; + speed(iBadOriented, :) = NaN; + direction(iBadOriented, :) = NaN; + verticalDist(iBadOriented, :) = NaN; + profileErrorCode(iBadOriented, :) = NaN; + qcFlag(iBadOriented, :) = NaN; +end dims = { - 'TIME', time(iWellOriented), ['Time stamp corresponds to the start of the measurement which lasts ' num2str(user.AvgInterval) ' seconds.']; ... - 'DIST_ALONG_BEAMS', distance, 'Nortek instrument data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' + 'TIME', time, ['Time stamp corresponds to the start of the measurement which lasts ' num2str(user.AvgInterval) ' seconds.']; ... + 'DIST_ALONG_BEAMS', distance, 'Nortek instrument data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' }; clear time distance; @@ -257,18 +279,18 @@ 'LATITUDE', [], NaN; ... 'LONGITUDE', [], NaN; ... 'NOMINAL_DEPTH', [], NaN; ... - 'VCUR_MAG', [1 iDimVel], velocity2(iWellOriented, :); ... % V - 'UCUR_MAG', [1 iDimVel], velocity1(iWellOriented, :); ... % U - 'WCUR', [1 iDimVel], velocity3(iWellOriented, :); ... - 'ABSIC1', [1 iDimDiag], backscatter1(iWellOriented, :); ... - 'ABSIC2', [1 iDimDiag], backscatter2(iWellOriented, :); ... - 'ABSIC3', [1 iDimDiag], backscatter3(iWellOriented, :); ... - 'TEMP', 1, temperature(iWellOriented); ... - 'PRES_REL', 1, pressure(iWellOriented); ... - 'VOLT', 1, battery(iWellOriented); ... - 'PITCH', 1, pitch(iWellOriented); ... - 'ROLL', 1, roll(iWellOriented); ... - 'HEADING_MAG', 1, heading(iWellOriented) + 'VCUR_MAG', [1 iDimVel], velocity2; ... % V + 'UCUR_MAG', [1 iDimVel], velocity1; ... % U + 'WCUR', [1 iDimVel], velocity3; ... + 'ABSIC1', [1 iDimDiag], backscatter1; ... + 'ABSIC2', [1 iDimDiag], backscatter2; ... + 'ABSIC3', [1 iDimDiag], backscatter3; ... + 'TEMP', 1, temperature; ... + 'PRES_REL', 1, pressure; ... + 'VOLT', 1, battery; ... + 'PITCH', 1, pitch; ... + 'ROLL', 1, roll; ... + 'HEADING_MAG', 1, heading }; clear analn1 analn2 time distance velocity1 velocity2 velocity3 ... backscatter1 backscatter2 backscatter3 ... @@ -277,20 +299,20 @@ if velocityProcessed % velocity has been processed vars = [vars; { - 'SNR1', [1 iDimDiag], sig2noise1(iWellOriented, :); ... - 'SNR2', [1 iDimDiag], sig2noise2(iWellOriented, :); ... - 'SNR3', [1 iDimDiag], sig2noise3(iWellOriented, :); ... -% 'STDB1', [1 iDimDiag], stdDev1(iWellOriented, :); ... % currently not used -% 'STDB2', [1 iDimDiag], stdDev2(iWellOriented, :); ... -% 'STDB3', [1 iDimDiag], stdDev3(iWellOriented, :); ... - 'NORTEK_ERR1', [1 iDimDiag], errorCode1(iWellOriented, :); ... - 'NORTEK_ERR2', [1 iDimDiag], errorCode2(iWellOriented, :); ... - 'NORTEK_ERR3', [1 iDimDiag], errorCode3(iWellOriented, :); ... - 'CSPD', [1 iDimVel], speed(iWellOriented, :); ... - 'CDIR_MAG', [1 iDimVel], direction(iWellOriented, :); ... -% 'VERT_DIST', [1 iDimVel], verticalDist(iWellOriented, :); ... % don't know what this is - 'NORTEK_PROFILE_ERR', [1 iDimVel], profileErrorCode(iWellOriented, :); ... - 'NORTEK_QC', [1 iDimVel], qcFlag(iWellOriented, :) + 'SNR1', [1 iDimDiag], sig2noise1; ... + 'SNR2', [1 iDimDiag], sig2noise2; ... + 'SNR3', [1 iDimDiag], sig2noise3; ... +% 'STDB1', [1 iDimDiag], stdDev1; ... % currently not used +% 'STDB2', [1 iDimDiag], stdDev2; ... +% 'STDB3', [1 iDimDiag], stdDev3; ... + 'NORTEK_ERR1', [1 iDimDiag], errorCode1; ... + 'NORTEK_ERR2', [1 iDimDiag], errorCode2; ... + 'NORTEK_ERR3', [1 iDimDiag], errorCode3; ... + 'CSPD', [1 iDimVel], speed; ... + 'CDIR_MAG', [1 iDimVel], direction; ... +% 'VERT_DIST', [1 iDimVel], verticalDist; ... % don't know what this is + 'NORTEK_PROFILE_ERR', [1 iDimVel], profileErrorCode; ... + 'NORTEK_QC', [1 iDimVel], qcFlag }]; clear sig2noise1 sig2noise2 sig2noise3 stdDev1 stdDev2 stdDev3 ... errorCode1 errorCode2 errorCode3 speed direction verticalDist ... diff --git a/Parser/awacParse.m b/Parser/awacParse.m index dd46adda6..e007a247c 100644 --- a/Parser/awacParse.m +++ b/Parser/awacParse.m @@ -216,10 +216,34 @@ height = -height; distance = -distance; end -iWellOriented = adcpOrientations == adcpOrientation; % we'll only keep data collected when ADCP is oriented as expected + +iBadOriented = adcpOrientations ~= adcpOrientation; % we'll only keep velocity data collected when ADCP is oriented as expected +velocity1(iBadOriented, :) = NaN; +velocity2(iBadOriented, :) = NaN; +velocity3(iBadOriented, :) = NaN; +backscatter1(iBadOriented, :) = NaN; +backscatter2(iBadOriented, :) = NaN; +backscatter3(iBadOriented, :) = NaN; +if velocityProcessed + sig2noise1(iBadOriented, :) = NaN; + sig2noise2(iBadOriented, :) = NaN; + sig2noise3(iBadOriented, :) = NaN; + stdDev1(iBadOriented, :) = NaN; + stdDev2(iBadOriented, :) = NaN; + stdDev3(iBadOriented, :) = NaN; + errorCode1(iBadOriented, :) = NaN; + errorCode2(iBadOriented, :) = NaN; + errorCode3(iBadOriented, :) = NaN; + speed(iBadOriented, :) = NaN; + direction(iBadOriented, :) = NaN; + verticalDist(iBadOriented, :) = NaN; + profileErrorCode(iBadOriented, :) = NaN; + qcFlag(iBadOriented, :) = NaN; +end + dims = { - 'TIME', time(iWellOriented), ['Time stamp corresponds to the start of the measurement which lasts ' num2str(user.AvgInterval) ' seconds.']; ... - 'DIST_ALONG_BEAMS', distance, 'Nortek instrument data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' + 'TIME', time, ['Time stamp corresponds to the start of the measurement which lasts ' num2str(user.AvgInterval) ' seconds.']; ... + 'DIST_ALONG_BEAMS', distance, 'Nortek instrument data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' }; clear time distance; @@ -258,18 +282,18 @@ 'LATITUDE', [], NaN; ... 'LONGITUDE', [], NaN; ... 'NOMINAL_DEPTH', [], NaN; ... - 'VCUR_MAG', [1 iDimVel], velocity2(iWellOriented, :); ... % V - 'UCUR_MAG', [1 iDimVel], velocity1(iWellOriented, :); ... % U - 'WCUR', [1 iDimVel], velocity3(iWellOriented, :); ... - 'ABSIC1', [1 iDimDiag], backscatter1(iWellOriented, :); ... - 'ABSIC2', [1 iDimDiag], backscatter2(iWellOriented, :); ... - 'ABSIC3', [1 iDimDiag], backscatter3(iWellOriented, :); ... - 'TEMP', 1, temperature(iWellOriented); ... - 'PRES_REL', 1, pressure(iWellOriented); ... - 'VOLT', 1, battery(iWellOriented); ... - 'PITCH', 1, pitch(iWellOriented); ... - 'ROLL', 1, roll(iWellOriented); ... - 'HEADING_MAG', 1, heading(iWellOriented) + 'VCUR_MAG', [1 iDimVel], velocity2; ... % V + 'UCUR_MAG', [1 iDimVel], velocity1; ... % U + 'WCUR', [1 iDimVel], velocity3; ... + 'ABSIC1', [1 iDimDiag], backscatter1; ... + 'ABSIC2', [1 iDimDiag], backscatter2; ... + 'ABSIC3', [1 iDimDiag], backscatter3; ... + 'TEMP', 1, temperature; ... + 'PRES_REL', 1, pressure; ... + 'VOLT', 1, battery; ... + 'PITCH', 1, pitch; ... + 'ROLL', 1, roll; ... + 'HEADING_MAG', 1, heading }; clear analn1 analn2 time distance velocity1 velocity2 velocity3 ... backscatter1 backscatter2 backscatter3 ... @@ -278,20 +302,20 @@ if velocityProcessed % velocity has been processed vars = [vars; { - 'SNR1', [1 iDimDiag], sig2noise1(iWellOriented, :); ... - 'SNR2', [1 iDimDiag], sig2noise2(iWellOriented, :); ... - 'SNR3', [1 iDimDiag], sig2noise3(iWellOriented, :); ... -% 'STDB1', [1 iDimDiag], stdDev1(iWellOriented, :); ... % currently not used -% 'STDB2', [1 iDimDiag], stdDev2(iWellOriented, :); ... -% 'STDB3', [1 iDimDiag], stdDev3(iWellOriented, :); ... - 'NORTEK_ERR1', [1 iDimDiag], errorCode1(iWellOriented, :); ... - 'NORTEK_ERR2', [1 iDimDiag], errorCode2(iWellOriented, :); ... - 'NORTEK_ERR3', [1 iDimDiag], errorCode3(iWellOriented, :); ... - 'CSPD', [1 iDimVel], speed(iWellOriented, :); ... - 'CDIR_MAG', [1 iDimVel], direction(iWellOriented, :); ... -% 'VERT_DIST', [1 iDimVel], verticalDist(iWellOriented, :); ... % don't know what this is - 'NORTEK_PROFILE_ERR', [1 iDimVel], profileErrorCode(iWellOriented, :); ... - 'NORTEK_QC', [1 iDimVel], qcFlag(iWellOriented, :) + 'SNR1', [1 iDimDiag], sig2noise1; ... + 'SNR2', [1 iDimDiag], sig2noise2; ... + 'SNR3', [1 iDimDiag], sig2noise3; ... +% 'STDB1', [1 iDimDiag], stdDev1; ... % currently not used +% 'STDB2', [1 iDimDiag], stdDev2; ... +% 'STDB3', [1 iDimDiag], stdDev3; ... + 'NORTEK_ERR1', [1 iDimDiag], errorCode1; ... + 'NORTEK_ERR2', [1 iDimDiag], errorCode2; ... + 'NORTEK_ERR3', [1 iDimDiag], errorCode3; ... + 'CSPD', [1 iDimVel], speed; ... + 'CDIR_MAG', [1 iDimVel], direction; ... +% 'VERT_DIST', [1 iDimVel], verticalDist; ... % don't know what this is + 'NORTEK_PROFILE_ERR', [1 iDimVel], profileErrorCode; ... + 'NORTEK_QC', [1 iDimVel], qcFlag }]; clear sig2noise1 sig2noise2 sig2noise3 stdDev1 stdDev2 stdDev3 ... errorCode1 errorCode2 errorCode3 speed direction verticalDist ... @@ -408,7 +432,7 @@ sample_data{2}.variables{i}.name = vars{i, 1}; sample_data{2}.variables{i}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(vars{i, 1}, 'type'))); sample_data{2}.variables{i}.dimensions = vars{i, 2}; - if ~isempty(vars{i, 2}) && ~strcmpi(vars{i, 1}, 'SPCT') % we don't want this for scalar variables nor SPCT + if ~isempty(vars{i, 2}) % we don't want this for scalar variables if any(strcmpi(vars{i, 1}, {'VDEN', 'SSWD_MAG', 'WSSH', 'VAVT', 'VDIR_MAG', 'SSDS_MAG', 'SSWV_MAG'})) sample_data{2}.variables{i}.coordinates = 'TIME LATITUDE LONGITUDE'; % data at the surface, can be inferred from standard/long names else diff --git a/Parser/continentalParse.m b/Parser/continentalParse.m index 00ac0bb86..38260a718 100644 --- a/Parser/continentalParse.m +++ b/Parser/continentalParse.m @@ -217,10 +217,32 @@ height = -height; distance = -distance; end -iWellOriented = adcpOrientations == adcpOrientation; % we'll only keep data collected when ADCP is oriented as expected +iBadOriented = adcpOrientations ~= adcpOrientation; % we'll only keep velocity data collected when ADCP is oriented as expected +velocity2(iBadOriented, :) = NaN; +velocity1(iBadOriented, :) = NaN; +velocity3(iBadOriented, :) = NaN; +backscatter1(iBadOriented, :) = NaN; +backscatter2(iBadOriented, :) = NaN; +backscatter3(iBadOriented, :) = NaN; +if velocityProcessed + sig2noise1(iBadOriented, :) = NaN; + sig2noise2(iBadOriented, :) = NaN; + sig2noise3(iBadOriented, :) = NaN; + stdDev1(iBadOriented, :) = NaN; + stdDev2(iBadOriented, :) = NaN; + stdDev3(iBadOriented, :) = NaN; + errorCode1(iBadOriented, :) = NaN; + errorCode2(iBadOriented, :) = NaN; + errorCode3(iBadOriented, :) = NaN; + speed(iBadOriented, :) = NaN; + direction(iBadOriented, :) = NaN; + verticalDist(iBadOriented, :) = NaN; + profileErrorCode(iBadOriented, :) = NaN; + qcFlag(iBadOriented, :) = NaN; +end dims = { - 'TIME', time(iWellOriented), ['Time stamp corresponds to the start of the measurement which lasts ' num2str(user.AvgInterval) ' seconds.']; ... - 'DIST_ALONG_BEAMS', distance, 'Nortek instrument data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' + 'TIME', time, ['Time stamp corresponds to the start of the measurement which lasts ' num2str(user.AvgInterval) ' seconds.']; ... + 'DIST_ALONG_BEAMS', distance, 'Nortek instrument data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' }; clear time distance; @@ -259,18 +281,18 @@ 'LATITUDE', [], NaN; ... 'LONGITUDE', [], NaN; ... 'NOMINAL_DEPTH', [], NaN; ... - 'VCUR_MAG', [1 iDimVel], velocity2(iWellOriented, :); ... % V - 'UCUR_MAG', [1 iDimVel], velocity1(iWellOriented, :); ... % U - 'WCUR', [1 iDimVel], velocity3(iWellOriented, :); ... - 'ABSIC1', [1 iDimDiag], backscatter1(iWellOriented, :); ... - 'ABSIC2', [1 iDimDiag], backscatter2(iWellOriented, :); ... - 'ABSIC3', [1 iDimDiag], backscatter3(iWellOriented, :); ... - 'TEMP', 1, temperature(iWellOriented); ... - 'PRES_REL', 1, pressure(iWellOriented); ... - 'VOLT', 1, battery(iWellOriented); ... - 'PITCH', 1, pitch(iWellOriented); ... - 'ROLL', 1, roll(iWellOriented); ... - 'HEADING_MAG', 1, heading(iWellOriented) + 'VCUR_MAG', [1 iDimVel], velocity2; ... % V + 'UCUR_MAG', [1 iDimVel], velocity1; ... % U + 'WCUR', [1 iDimVel], velocity3; ... + 'ABSIC1', [1 iDimDiag], backscatter1; ... + 'ABSIC2', [1 iDimDiag], backscatter2; ... + 'ABSIC3', [1 iDimDiag], backscatter3; ... + 'TEMP', 1, temperature; ... + 'PRES_REL', 1, pressure; ... + 'VOLT', 1, battery; ... + 'PITCH', 1, pitch; ... + 'ROLL', 1, roll; ... + 'HEADING_MAG', 1, heading }; clear analn1 analn2 time distance velocity1 velocity2 velocity3 ... backscatter1 backscatter2 backscatter3 ... @@ -279,20 +301,20 @@ if velocityProcessed % velocity has been processed vars = [vars; { - 'SNR1', [1 iDimDiag], sig2noise1(iWellOriented, :); ... - 'SNR2', [1 iDimDiag], sig2noise2(iWellOriented, :); ... - 'SNR3', [1 iDimDiag], sig2noise3(iWellOriented, :); ... -% 'STDB1', [1 iDimDiag], stdDev1(iWellOriented, :); ... % currently not used -% 'STDB2', [1 iDimDiag], stdDev2(iWellOriented, :); ... -% 'STDB3', [1 iDimDiag], stdDev3(iWellOriented, :); ... - 'NORTEK_ERR1', [1 iDimDiag], errorCode1(iWellOriented, :); ... - 'NORTEK_ERR2', [1 iDimDiag], errorCode2(iWellOriented, :); ... - 'NORTEK_ERR3', [1 iDimDiag], errorCode3(iWellOriented, :); ... - 'CSPD', [1 iDimVel], speed(iWellOriented, :); ... - 'CDIR_MAG', [1 iDimVel], direction(iWellOriented, :); ... -% 'VERT_DIST', [1 iDimVel], verticalDist(iWellOriented, :); ... % don't know what this is - 'NORTEK_PROFILE_ERR', [1 iDimVel], profileErrorCode(iWellOriented, :); ... - 'NORTEK_QC', [1 iDimVel], qcFlag(iWellOriented, :) + 'SNR1', [1 iDimDiag], sig2noise1; ... + 'SNR2', [1 iDimDiag], sig2noise2; ... + 'SNR3', [1 iDimDiag], sig2noise3; ... +% 'STDB1', [1 iDimDiag], stdDev1; ... % currently not used +% 'STDB2', [1 iDimDiag], stdDev2; ... +% 'STDB3', [1 iDimDiag], stdDev3; ... + 'NORTEK_ERR1', [1 iDimDiag], errorCode1; ... + 'NORTEK_ERR2', [1 iDimDiag], errorCode2; ... + 'NORTEK_ERR3', [1 iDimDiag], errorCode3; ... + 'CSPD', [1 iDimVel], speed; ... + 'CDIR_MAG', [1 iDimVel], direction; ... +% 'VERT_DIST', [1 iDimVel], verticalDist; ... % don't know what this is + 'NORTEK_PROFILE_ERR', [1 iDimVel], profileErrorCode; ... + 'NORTEK_QC', [1 iDimVel], qcFlag }]; clear sig2noise1 sig2noise2 sig2noise3 stdDev1 stdDev2 stdDev3 ... errorCode1 errorCode2 errorCode3 speed direction verticalDist ... diff --git a/Parser/readWQMdat.m b/Parser/readWQMdat.m index 9a1086d05..01593cb08 100644 --- a/Parser/readWQMdat.m +++ b/Parser/readWQMdat.m @@ -25,7 +25,7 @@ % CHL(ug/l) (floating point chlorophyll, micrograms/Litre) % CHLa(ug/l) (floating point chlorophyll, micrograms/Litre) % F-Cal-CHL(ug/l) (floating point factory coefficient chlorophyll, micrograms/Litre) -% Fact-CHL(ug/l)) (floating point factory coefficient chlorophyll, micrograms/Litre) +% Fact-CHL(ug/l) (floating point factory coefficient chlorophyll, micrograms/Litre) % U-Cal-CHL(ug/l) (floating point user coefficient chlorophyll, micrograms/Litre) % RawCHL(Counts) (integer fluorescence in raw counts) % CHLa(Counts) (integer fluorescence in raw counts) @@ -150,7 +150,7 @@ 'photodetector paired with an optical filter which measures everything '... 'that fluoresces in the region of 695nm. '... 'Originally expressed in ug/l, 1l = 0.001m3 was assumed.'}}; - params{end+1} = {'Fact-CHL(ug/l))', {'CHLF', 'Artificial chlorophyll data '... + params{end+1} = {'Fact-CHL(ug/l)', {'CHLF', 'Artificial chlorophyll data '... 'computed from bio-optical sensor raw counts measurements using factory calibration coefficient. The '... 'fluorometre is equipped with a 470nm peak wavelength LED to irradiate and a '... 'photodetector paired with an optical filter which measures everything '... @@ -497,6 +497,9 @@ case upper('DO(mg/l)') % DOX1_3 end end +% handle potential unlabelled last column +format=strcat(format,'%*s'); + %remove unsupported fields from header list fields(unsupported) = []; end diff --git a/Parser/readXR420.m b/Parser/readXR420.m index 8eba09749..dd9e6a216 100644 --- a/Parser/readXR420.m +++ b/Parser/readXR420.m @@ -369,7 +369,7 @@ line = fgetl(fid); % a single blank line separates the header from the data - while ~isempty(line) + while ~isempty(line) && ischar(line) lines = [lines line]; line = fgetl(fid); diff --git a/Parser/readXR620.m b/Parser/readXR620.m index 22d614d55..c9a5c176a 100644 --- a/Parser/readXR620.m +++ b/Parser/readXR620.m @@ -723,7 +723,7 @@ line = fgetl(fid); - while isempty(strfind(line, 'Date & Time')) + while isempty(strfind(line, 'Date & Time')) && ischar(line) lines = [lines line]; line = fgetl(fid); end diff --git a/Parser/signatureParse.m b/Parser/signatureParse.m index 6840a6f00..249f86d2c 100644 --- a/Parser/signatureParse.m +++ b/Parser/signatureParse.m @@ -275,10 +275,22 @@ % case of a downward looking ADCP -> negative values distance = -distance; end -iWellOriented = all(adcpOrientations == repmat(adcpOrientation, nSamples, 1), 2); % we'll only keep data collected when ADCP is oriented as expected +iBadOriented = any(adcpOrientations ~= repmat(adcpOrientation, nSamples, 1), 2); % we'll only keep velocity data collected when ADCP is oriented as expected +Velocity_N(iBadOriented, :) = NaN; +Velocity_E(iBadOriented, :) = NaN; +Velocity_U(iBadOriented, :) = NaN; +Velocity_U2(iBadOriented, :) = NaN; +Backscatter1(iBadOriented, :) = NaN; +Backscatter2(iBadOriented, :) = NaN; +Backscatter3(iBadOriented, :) = NaN; +Backscatter4(iBadOriented, :) = NaN; +Correlation1(iBadOriented, :) = NaN; +Correlation2(iBadOriented, :) = NaN; +Correlation3(iBadOriented, :) = NaN; +Correlation4(iBadOriented, :) = NaN; dims = { - 'TIME', Time(iWellOriented), ''; ... - 'DIST_ALONG_BEAMS', distance, 'Nortek instrument data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' + 'TIME', Time, ''; ... + 'DIST_ALONG_BEAMS', distance, 'Nortek instrument data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' }; clear Time distance; @@ -306,25 +318,25 @@ 'LATITUDE', [], NaN; ... 'LONGITUDE', [], NaN; ... 'NOMINAL_DEPTH', [], NaN; ... - ['VCUR' magExt], [1 iDimVel], Velocity_N(iWellOriented, :); ... % V - ['UCUR' magExt], [1 iDimVel], Velocity_E(iWellOriented, :); ... % U - 'WCUR', [1 iDimVel], Velocity_U(iWellOriented, :); ... - 'WCUR_2', [1 iDimVel], Velocity_U2(iWellOriented, :); ... - 'ABSIC1', [1 iDimDiag], Backscatter1(iWellOriented, :); ... - 'ABSIC2', [1 iDimDiag], Backscatter2(iWellOriented, :); ... - 'ABSIC3', [1 iDimDiag], Backscatter3(iWellOriented, :); ... - 'ABSIC4', [1 iDimDiag], Backscatter4(iWellOriented, :); ... - 'CMAG1', [1 iDimDiag], Correlation1(iWellOriented, :); ... - 'CMAG2', [1 iDimDiag], Correlation2(iWellOriented, :); ... - 'CMAG3', [1 iDimDiag], Correlation3(iWellOriented, :); ... - 'CMAG4', [1 iDimDiag], Correlation4(iWellOriented, :); ... - 'TEMP', 1, Temperature(iWellOriented); ... - 'PRES_REL', 1, Pressure(iWellOriented); ... - 'SSPD', 1, speedOfSound(iWellOriented); ... - 'VOLT', 1, Battery(iWellOriented); ... - 'PITCH', 1, Pitch(iWellOriented); ... - 'ROLL', 1, Roll(iWellOriented); ... - ['HEADING' magExt], 1, Heading(iWellOriented) + ['VCUR' magExt], [1 iDimVel], Velocity_N; ... % V + ['UCUR' magExt], [1 iDimVel], Velocity_E; ... % U + 'WCUR', [1 iDimVel], Velocity_U; ... + 'WCUR_2', [1 iDimVel], Velocity_U2; ... + 'ABSIC1', [1 iDimDiag], Backscatter1; ... + 'ABSIC2', [1 iDimDiag], Backscatter2; ... + 'ABSIC3', [1 iDimDiag], Backscatter3; ... + 'ABSIC4', [1 iDimDiag], Backscatter4; ... + 'CMAG1', [1 iDimDiag], Correlation1; ... + 'CMAG2', [1 iDimDiag], Correlation2; ... + 'CMAG3', [1 iDimDiag], Correlation3; ... + 'CMAG4', [1 iDimDiag], Correlation4; ... + 'TEMP', 1, Temperature; ... + 'PRES_REL', 1, Pressure; ... + 'SSPD', 1, speedOfSound; ... + 'VOLT', 1, Battery; ... + 'PITCH', 1, Pitch; ... + 'ROLL', 1, Roll; ... + ['HEADING' magExt], 1, Heading }; clear Velocity_N Velocity_E Velocity_U Velocity_U2 ... Backscatter1 Backscatter2 Backscatter3 Backscatter4 ... diff --git a/Parser/workhorseParse.m b/Parser/workhorseParse.m index e5319beb7..40c173272 100644 --- a/Parser/workhorseParse.m +++ b/Parser/workhorseParse.m @@ -273,11 +273,29 @@ height = -height; distance = -distance; end - iWellOriented = adcpOrientations == adcpOrientation; % we'll only keep data collected when ADCP is oriented as expected + iBadOriented = adcpOrientations ~= adcpOrientation; % we'll only keep velocity data collected when ADCP is oriented as expected + vnrth(iBadOriented, :) = NaN; + veast(iBadOriented, :) = NaN; + wvel(iBadOriented, :) = NaN; + evel(iBadOriented, :) = NaN; + speed(iBadOriented, :) = NaN; + direction(iBadOriented, :) = NaN; + backscatter1(iBadOriented, :) = NaN; + backscatter2(iBadOriented, :) = NaN; + backscatter3(iBadOriented, :) = NaN; + backscatter4(iBadOriented, :) = NaN; + correlation1(iBadOriented, :) = NaN; + correlation2(iBadOriented, :) = NaN; + correlation3(iBadOriented, :) = NaN; + correlation4(iBadOriented, :) = NaN; + percentGood1(iBadOriented, :) = NaN; + percentGood2(iBadOriented, :) = NaN; + percentGood3(iBadOriented, :) = NaN; + percentGood4(iBadOriented, :) = NaN; dims = { - 'TIME', time(iWellOriented), ['Time stamp corresponds to the start of the measurement which lasts ' num2str(sample_data.meta.instrument_average_interval) ' seconds.']; ... - 'HEIGHT_ABOVE_SENSOR', height, 'Data has been vertically bin-mapped using tilt information so that the cells have consistant heights above sensor in time.'; ... - 'DIST_ALONG_BEAMS', distance, 'Data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' + 'TIME', time, ['Time stamp corresponds to the start of the measurement which lasts ' num2str(sample_data.meta.instrument_average_interval) ' seconds.']; ... + 'HEIGHT_ABOVE_SENSOR', height, 'Data has been vertically bin-mapped using tilt information so that the cells have consistant heights above sensor in time.'; ... + 'DIST_ALONG_BEAMS', distance, 'Data is not vertically bin-mapped (no tilt correction applied). Cells are lying parallel to the beams, at heights above sensor that vary with tilt.' }; clear time height distance; @@ -306,30 +324,30 @@ 'LATITUDE', [], NaN; ... 'LONGITUDE', [], NaN; ... 'NOMINAL_DEPTH', [], NaN; ... - ['VCUR' magExt], [1 2], vnrth(iWellOriented, :); ... - ['UCUR' magExt], [1 2], veast(iWellOriented, :); ... - 'WCUR', [1 2], wvel(iWellOriented, :); ... - 'ECUR', [1 2], evel(iWellOriented, :); ... - 'CSPD', [1 2], speed(iWellOriented, :); ... - ['CDIR' magExt], [1 2], direction(iWellOriented, :); ... - 'ABSIC1', [1 3], backscatter1(iWellOriented, :); ... - 'ABSIC2', [1 3], backscatter2(iWellOriented, :); ... - 'ABSIC3', [1 3], backscatter3(iWellOriented, :); ... - 'ABSIC4', [1 3], backscatter4(iWellOriented, :); ... - 'TEMP', 1, temperature(iWellOriented); ... - 'PRES_REL', 1, pressure(iWellOriented); ... - 'PSAL', 1, salinity(iWellOriented); ... - 'CMAG1', [1 3], correlation1(iWellOriented, :); ... - 'CMAG2', [1 3], correlation2(iWellOriented, :); ... - 'CMAG3', [1 3], correlation3(iWellOriented, :); ... - 'CMAG4', [1 3], correlation4(iWellOriented, :); ... - 'PERG1', [1 2], percentGood1(iWellOriented, :); ... - 'PERG2', [1 2], percentGood2(iWellOriented, :); ... - 'PERG3', [1 2], percentGood3(iWellOriented, :); ... - 'PERG4', [1 2], percentGood4(iWellOriented, :); ... - 'PITCH', 1, pitch(iWellOriented); ... - 'ROLL', 1, roll(iWellOriented); ... - ['HEADING' magExt], 1, heading(iWellOriented) + ['VCUR' magExt], [1 2], vnrth; ... + ['UCUR' magExt], [1 2], veast; ... + 'WCUR', [1 2], wvel; ... + 'ECUR', [1 2], evel; ... + 'CSPD', [1 2], speed; ... + ['CDIR' magExt], [1 2], direction; ... + 'ABSIC1', [1 3], backscatter1; ... + 'ABSIC2', [1 3], backscatter2; ... + 'ABSIC3', [1 3], backscatter3; ... + 'ABSIC4', [1 3], backscatter4; ... + 'TEMP', 1, temperature; ... + 'PRES_REL', 1, pressure; ... + 'PSAL', 1, salinity; ... + 'CMAG1', [1 3], correlation1; ... + 'CMAG2', [1 3], correlation2; ... + 'CMAG3', [1 3], correlation3; ... + 'CMAG4', [1 3], correlation4; ... + 'PERG1', [1 2], percentGood1; ... + 'PERG2', [1 2], percentGood2; ... + 'PERG3', [1 2], percentGood3; ... + 'PERG4', [1 2], percentGood4; ... + 'PITCH', 1, pitch; ... + 'ROLL', 1, roll; ... + ['HEADING' magExt], 1, heading }; clear vnrth veast wvel evel speed direction backscatter1 ... diff --git a/Util/fastScatterMesh.m b/Util/fastScatterMesh.m index 5e4c450fe..a9f0fbf40 100644 --- a/Util/fastScatterMesh.m +++ b/Util/fastScatterMesh.m @@ -20,26 +20,17 @@ minClim = colLimits(1); maxClim = colLimits(2); - -% Posting by Boris Babic for the method. see http://www.mathworks.com/matlabcentral/newsreader/view_thread/22966 -% as referenced in -% http://au.mathworks.com/matlabcentral/fileexchange/47205-fastscatter-m -% http://au.mathworks.com/matlabcentral/fileexchange/53580-fastscatterm % index of valid data -ix=find(isfinite(cdata + xdata + ydata)); - -if isempty(ix) | numel(ix) < 2 - hwin = []; +ix = find(isfinite(cdata + xdata + ydata)); +if isempty(ix) || numel(ix) < 2 return; end -if mod(length(ix),2)==1 - ix(end+1)=ix(end); +if mod(length(ix), 2) == 1 + ix(end+1) = ix(end); end -ixLength = length(ix); - % normalize cdata normData = (cdata(ix) - minClim) / ( maxClim - minClim ); [~, idx] = histc(normData, 0: 1/nColors : 1); @@ -55,26 +46,26 @@ switch zType case 'ascending', - zBuffer = linspace(0, 1, ixLength); + zBuffer = linspace(0, 1, nColors); case 'descending', - zBuffer = linspace(1, 0, ixLength) ; + zBuffer = linspace(1, 0, nColors) ; case 'triangle', - zBuffer = [linspace(0, 1, ixLength/2) linspace(1, 0, ixLength/2)]; + zBuffer = [linspace(0, 1, nColors/2) linspace(1, 0, nColors/2)]; case 'vee', - zBuffer = [linspace(1, 0, ixLength/2) linspace(0, 1, ixLength/2)]; + zBuffer = [linspace(1, 0, nColors/2) linspace(0, 1, nColors/2)]; case 'flat', zBuffer = zeros(size(ix)); case 'parabolic' - jj = (0:ixLength-1)'; - zBuffer = 1 - (2*jj/ixLength - 1).^2; + jj = (0:nColors-1)'; + zBuffer = 1 - (2*jj/nColors - 1).^2; case 'hamming' - thalf=pi/ixLength*((1-ixLength):2:0)'; % first half sample locations + thalf=pi/nColors*((1-nColors):2:0)'; % first half sample locations hwin=.54+.46*cos(thalf); % first half window - zBuffer=[hwin; hwin(floor(ixLength/2):-1:1)]; % full window + zBuffer=[hwin; hwin(floor(nColors/2):-1:1)]; % full window case 'hann' - thalf=pi/ixLength*((1-ixLength):2:0)'; % first half sample locations + thalf=pi/nColors*((1-nColors):2:0)'; % first half sample locations hwin=.5+.5*cos(thalf); % first half window - zBuffer=[hwin; hwin(floor(ixLength/2):-1:1)]; % full window + zBuffer=[hwin; hwin(floor(nColors/2):-1:1)]; % full window otherwise warning('Unknown marker zbuffer method, using flat'); zBuffer = zeros(size(ix)); diff --git a/imosToolbox.m b/imosToolbox.m index 8a37cd47f..28178174f 100644 --- a/imosToolbox.m +++ b/imosToolbox.m @@ -73,7 +73,7 @@ function imosToolbox(auto, varargin) end % Set current toolbox version -toolboxVersion = ['2.5.24 - ' computer]; +toolboxVersion = ['2.5.25 - ' computer]; switch auto case 'auto', autoIMOSToolbox(toolboxVersion, varargin{:}); diff --git a/imosToolbox_Linux64.bin b/imosToolbox_Linux64.bin index ef3e0fa8c..97daa97e5 100755 Binary files a/imosToolbox_Linux64.bin and b/imosToolbox_Linux64.bin differ diff --git a/imosToolbox_Win32.exe b/imosToolbox_Win32.exe index c657bb858..cc236fd7b 100644 Binary files a/imosToolbox_Win32.exe and b/imosToolbox_Win32.exe differ diff --git a/imosToolbox_Win64.exe b/imosToolbox_Win64.exe index b762808ba..144d93be7 100644 Binary files a/imosToolbox_Win64.exe and b/imosToolbox_Win64.exe differ