SOCIB RAMADDA Data Repository
Click to show menu
InformationAdd/View CommentsAdd to Cart
The aim of this wiki page is to collect anything related with Octave and MATLAB that is worth to spread. Coding style proposals, tips and tricks, packages and toolboxes, references, contributed functions and scripts... everything is welcome.

Coding style

This is an initial proposal.

Naming conventions

Code entities:
  • Conventional variables are under_scored.
  • Struct field names are under_scored.
  • Function arguments are under_scored.
  • Global constant variables are ALL_CAPITALS.
  • Functions are camelCased (first letter lower case).
  • Class names are CamelCased (first letter upper case).

File system entities:
  • Function files match function name, so they are camelCased.
  • Script files are under_scored.
  • Any other files (data sets, configuration files...) are under_scored.

Code layout

  • Indent your code.
  • Use spaces for indentation.
  • Use 2 (two) spaces per indentation level.
  • Do not use a delimiter after the conditions, switch cases or else and end keywords in control statements (at the end of a line).

Functions and scripts

  • Prefer subfunctions over nested functions (when possible).
  • Avoid using clear inside functions and scripts. Instead, use local variables and initialize/reset them properly with an assignment when needed.
  • Try to avoid global variables. Use them only when really needed.

Comments

  • Comments are sentences, so they must start with a capital letter and end with a proper punctuation mark (usually a full stop, sometimes a colon).
  • Indent the comments with the same level than the code they are related with.
  • Documentation comments are mandatory for all scripts and functions, even subfunctions and nested functions.
  • Documentation header comments should look like documentation comments of general functions shipped with MATLAB/Octave. In particular, use CAPITALS to refer to input and output arguments and to other MATLAB/Octave entities like functions or scripts. This allows the built-in help support system to work with your functions.

Tips and tricks

Function and script templates

To get a consistent template with documentation comment headers for all your functions and scripts you may want to use the functions fedit and sedit. Place them in your path. To write a new function called myfun type fedit myfun or fedit 'myfun' 'argin1, argin2' 'argout1, argout2' and start documenting and coding your function. Author and email fields are taken from your Git configuration. Edit the template functions to fill these fields if you do not use Git (or better, give Git a try and you will not regret it!).
function output = myfun(input)
%MYFUN  One-line description here, please.
%
%  Syntax:
%    OUTPUT = MYFUN(INPUT)
%
%  Description:
%    OUTPUT = MYFUN(INPUT) Detailed description here, please.
%
%  Notes:
%
%  Examples:
%    output = myfun(input)
%
%  See also:
%
%  Authors:
%    Joan Pau Beltran  

% Copyright (C) 2015 % ICTS SOCIB - Servei d'observacio i prediccio costaner de les Illes Balears. % % % This program is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % (at your option) any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program. If not, see .

end

Deal with file system entities (paths, folder and file names)

Never build paths manually by string concatenation. Use the function fullfile instead. It ensures that the scripts will run on different platforms. Similarly, use fileparts to decompose a file path into folder, file name and extension.
full_file_name = 'path/to/file'                  % NO: *IX only.
full_file_name = fullfile('path', 'to', 'file')  % YES: portable.

Use the PostgreSQL Java driver from MATLAB

The JDBC driver may be installed as a package from the distribution repositories: libpg-java (renamed to libpostgresql-jdbc-java on Debian repositories on March 2012).
$ sudo aptitude install libpg-java
It provides a jar file /usr/share/java/postgresql.jar that should be added to the java class path.
$ dpkg -L libpg-java
/.
/usr
/usr/share
/usr/share/java
/usr/share/java/postgresql-jdbc3-8.4.jar
/usr/share/doc
/usr/share/doc/libpg-java
/usr/share/doc/libpg-java/README
/usr/share/doc/libpg-java/copyright
/usr/share/doc/libpg-java/changelog.Debian.gz
/usr/share/java/postgresql-jdbc3.jar
/usr/share/java/postgresql.jar
To use it in the current session it may be added to the dynamic java class path:
javaaddpath('/usr/share/java/postgresql.jar')
To set it up permanently for use it in every session add it to the static java class path editing the list in classpath.txt.
which classpath.txt
edit classpath.txt

Build mex files from MATLAB prompt with recent compiler versions

On GNU/Linux systems, build a mex file from inside a MATLAB session may fail after a warning if the compiler version is newer than the latest version supported by MATLAB, even though running the same mex command on a system shell might build the target properly. The reason is that MATLAB may extent or overwrite the environment variable LD_LIBRARY_PATH to point to its own version of the standard libraries, causing an incompatibility with the version of the compiler. To solve the problem, either build the target from the shell or temporarilyu overwrite the environment variable LD_LIBRARY_PATH from the MATLAB session.
ld_library_path = getenv('LD_LIBRARY_PATH')
setenv('LD_LIBRARY_PATH') % this resets the variable to emtpy ''.
% mex call here ...
setenv('LD_LIBRARY_PATH', ld_libray_path)
clear('ld_library_path')

Custom color maps from Gnuplot palettes

MATLAB and Octave provide very few built-in color maps. Gnuplot provides a way of defining custom color maps based on RGB palette formulas. This functionality has been ported in function gpcolormap.
peaks();
colorbar();
% Default Gnuplot color map:
colormap(gpcolormap());
% Custom color map printable on gray:
printable = gpcolormap(30, 31, 32);
colormap(printable);
% Custom color map with given number of colors:
rainbow = gpcolormap(33, 13, 10, 24);
colormap(rainbow);

Print figures with custom settings using ImageMagick

MATLAB figure export capabilities often pose problems on different platforms: non-antialiased fonts and/or lines on some image formats, bad resolution control, renderer related problems...

If some figure elements are not displayed or printed properly on Unix-alike systems, you may try to manually set the renderer to painters.
% Create a figure with a rich pseudo-color map:
pcolor(peaks());
colorbar();
% Set a title using a fancy font
% (it may not appear in the figure due to the renderer):
title('Fancy figure with a boring title font', ...
      'FontName', 'Courier', 'FontWeight', 'bold', ...
      'FontUnits', 'points', 'FontSize', 16);
% Print with automatically selected renderer:
print(gcf, 'unknown_renderer.eps', '-depsc2');
print(gcf, 'unknown_renderer.png', '-dpng');
% Print using the 'painters' renderer:
print(gcf, 'painters_renderer.eps', '-depsc2', '-painters');
print(gcf, 'painters_renderer.png', '-dpng', '-painters');
% Set the renderer figure property to 'painters'
% (the title may appear now):
set(gcf, 'Renderer', 'painters');

Also, when exporting to non-vectorial formats (png, jpg, bmp...) you may get better results by exporting first to some vectorial format (eps2, epsc2) and use an external program like convert from ImageMagick or GraphicsMagick to convert the vectorial image file to the desired format. The function printfigure automates that process offering a better control of the resolution and final image size:
% Export to png with printFigure:
printFigure(gcf, ...
  'filename', 'painters_renderer_printfigure', ...
  'format', 'png', 'render', 'painters');
% Export to png with printFigure for paper printing:
set(gcf, 'PaperUnits', 'inches', 'PaperSize', [6.40 3.83], ...
    'PaperPosition', [0 0 6.40 3.84]);
printfigure(gcf, ...
  'filename', 'painters_renderer_printfigure_300dpi', ...
  'format', 'png', 'render', 'painters', 'resolution', 300);
% Export to png with printFigure for the web:
set(gcf, 'PaperUnits', 'inches', 'PaperSize', [600 360]/90, ...
    'PaperPosition', [0 0 600 360]/90);
printfigure(gcf, ...
  'filename', 'painters_renderer_printfigure_90dpi_600x360', ...
  'format', 'png', 'render', 'painters', 'resolution', 90);

Arrange several plots on a grid layout

Even though the built-in function subplot already does that, it does not provide any way to control the amount of space left between plots. In addition, it indexes the cells of the grid layout in row-major order, which is not consisten with the rest of Octave/MATLAB.

The function arrange creates a grid layout honoring the desired margins, and also allows you to set the cells of the grid layout spanned by each plot using column-major indices (as anywhere else in MATLAB/Octave), or even row and column indices:
% Create some plots.
figure();
x = randn(200,1);
y = randn(100,1);
z = y*x';
ax1 = axes();
pcolor(ax1, z);
shading('interp');
colorbar();
title('Normal Bivariate');
ax2 = axes();
plot(y, 1:length(y));
ylabel('Normal Univariate');
ax3 = axes();
plot(1:length(x), x);
xlabel('Normal Univariate');
set([ax1 ax2 ax3], 'XTickLabel', {}, 'YTickLabel', {});
arrange( ...
 'axes', [ax1, ax2, ax3], ...
 'rows', 4, 'cols', 7, 'spans', {[1 2; 3 7], [1 1; 3 1], [4 2; 4 7]}, ...
 'top', 0.1, 'bottom', 0.1, 'left', 0.1, 'right', 0.2, ...
 'inrow', 0.02, 'incol', 0.02, 'position', 'Position');

Load and save data in NetCDF format

There are different NetCDF libraries for Octave and MATLAB. With the function loadnc it is really easy to load data from local NetCDF files or OPeNDAP links:
url = 'http://test.opendap.org:80/opendap/netcdf/examples/tos_O1_2001-2002.nc'
% Read all information at once:
[var_data, var_meta, global_meta] = loadnc(url)
% Retrieve data of interest without metadata:
var_data = loadnc(url, {'time', 'lon', 'lat', 'tos'})
% Retrieve data of interest with metadata:
[var_data, var_meta] = loadnc(url, {'time', 'lon', 'lat', 'tos'})
% Retrieve data renaming variables:
var_names = {'time', 'lon', 'lat', 'tos'};
new_names = {'time', 'longitude', 'latitude', 'temperature'};
var_data = loadnc(url, var_names, new_names)
Conversely, it is also easy to export data to a NetCDF file using savenc. Just gather the desired variables in a structure, and create two new structures defining their dimensions and attributes:
% Some example data (scalar, vector, and array):
var_data = struct()
var_data.rand_num = int8(round(12 * rand([1 1])));
var_data.rand_vec = randn([25 1]);
var_data.rand_mat = round(12 * rand([5 10]));
var_data.rand_mat(var_data.rand_mat == 1) = nan;
% A minimum file with no attributes:
global_meta = struct();
global_meta.dimensions = ...
  struct('name', {'dim1' 'dim2' 'dim3'}, 'length', {0 5 10});
global_meta.name = 'random.nc';
var_meta = struct():
var_meta.rand_num = struct('dimensions', { {} });
var_meta.rand_vec = struct('dimensions', { {'dim1'} });
var_meta.rand_mat = struct('dimensions', { {'dim2' 'dim3'} }, ...
                           'datatype', {'int'});
savenc(var_data, var_meta, global_meta);

% Add some attributes (_FillValue, add_offset and scale_factor are special): filename = 'random_with_atts.nc'; var_meta.rand_num.attributes = ... struct('name', {'comment'}, ... 'value', {'This is a random signed 8 bit integer'}) var_meta.rand_vec.attributes = ... struct('name', {'comment' 'add_offset' 'scale_factor'}, ... 'value', {'This is a linearly scaled random vector' 10 2.5}); var_meta.rand_mat.attributes = ... struct('name', {'comment' '_FillValue'}, ... 'value', {'This is a random matrix with invalid values' intmax()}); global_meta.attributes = ... struct('name', {'creation_date'}, 'value', {datestr(now)}); savenc(var_data, var_meta, global_meta, filename)

Load and save data in JSON format

There is no native support for JSON in Octave nor in MATLAB. Even though there are some JSON utils out there, they do not seem to escape/unescape text values properly. The custom made functions loadjson and savejson attempt to solve the issue. The following example shows the mapping between native data types and JSON values, also described in the documentation of the functions:
% Encode to string
m_values = { ...
  [] {} struct() 25 NaN true false rand(1,10) ...
  struct('field1', {'a', 'b', 'c'}, 'field2', {[1 2], [3 5], [4 3.14]}) ...
  sprintf(['escape this:' ...
           ' \b (backspace) \f (form feed) \n (line feed) \t (tab)' ...
           ' \r (carriage return) \x001A (escaped unicode character)' ...
           ' " (quotation mark) \\ (backslash)']) };
json_values = savejson(m_values)

% Encode to file: savejson(m_values, 'json/example.json')

% Decode from file: values_m = loadjson([], 'json/example.json')

% Decode from string: values_json = ... sprintf('[%s, %s, %s, %s, %s, %s, %s, %s, %s]', ... '[]', '{}', '25', '14.3', 'null', 'true', 'false', ... '{"field1": ["a", "b", "c"], "field2": [[1,2], [3,5], [4,3.14]]}', ... strcat('"unescape this: \b (backspace) \f (form feed)', ... ' \n (line feed) \t (tab) \r (carriage return)', ... ' \u001a (escaped unicode character)', ... ' \" (quotation mark) \\ (backslash)"'));

% Decode from file: values_m = loadjson(values_json)

This is a more realistic example loading deployment information in JSON format from SOCIB RESTful API:
request = 'http://apps.socib.es/DataDiscovery/list-deployments?platform_type=glider';
[response, status] = urlread(request);
if status ~= 1
  error('URLReadError', 'Error reading from url: %s.', request);
end
deployments = loadjson(response);

Connect to an SFTP remote server

Octave/MATLAB comes with a built-in FTP client, but there is no support for SFTP. The FTP protocol poses a security risk because the only authentication method is based on passwords (hardcoded somewhere in the script in plain text), and the traffic might not be encrypted. With SFTP, the traffic is encrypted, and when using public-private keys, there is no need to store any password. The sftp functions provide an old-style class for SFTP based on the libssh library, with the same methods of the native FTP objects.
% Assume known host 'mysftpserver.org' and public key authentication:
server = 'mysftpserver.org'
h = sftp(server)
% List the contents of the remote working directory:
dir(h)
% Change the remote working directory:
cd(h, path)
% Download file from remote working directory to current working directory:
mget(h, remotefile)
% Download file from remote working directory to another directory:
mget(h, remotefile, target)
% Download all files and directories in remote directory:
list = mget(h, '*')
% Download all hidden files and directories in remote directory, to a different directory:
list = mget(h, '.*', stash)
% Upload file(s) to the current remote working directory:
mput(h, localfile)
% Close the connection:
close(h)
All low level operations are implemented in the private mex file, which must be compiled. Compilation instructions are included in the header of the C source file mexsftp.c

MATLAB/Octave compatibility issues

You may want your functions and scripts to be compatible with both Octave and MATLAB, but sometimes the same functionality requires different code in each program. A workaround is to use a conditional test to insert the proper code depending on the interpreter:
ISOCTAVE = ~isempty(ver('Octave')); % seems slower
ISOCTAVE = exist('OCTAVE_VERSION','builtin'); % seems faster
if ISOCTAVE
  % Octave code
else
  % MATLAB code
end

General issues

  • First check the list here: [1]

NetCDF issues

  • The NetCDF API differs.
    • In Octave, it is provided by the package OCTCDF. It can perform fill value substitution and scaling on demand.
    • In MATLAB, it is provided by the suite SNCTOOLS. It always does fill value substitution and scaling.

There are two compatibility functions available, using the right set of functions depending on the interpreter: loadnc and savenc. Using them, a whole data set or a subset of its variables may be loaded with the same single function call from either Octave or MATLAB.
Powered by Geode Systems and RAMADDA