Contents
- Create netCDF-CF of orthogonal x-y grid
- Define meta-info: global: x,y sticks > lat,lon matrices
- Define dimensions/coordinates
- Define variable (define some data)
- 1.a Create netCDF file
- 1.b Add overall meta info
- 2.a Create matrix span dimensions
- 3.a Create coordinate vectors: x and y
- 3.b Create coordinate variables: coordinate system: epsg
- 3.c Create coordinate variables: longitude
- 3.d Create coordinate variables: latitude
- 3.e Create coordinate variables: coordinate system: WGS84 default
- 4 Create dependent variable
- 5.a Create all variables with attributes
- 5.b Fill all variables
- 6 Check file summary
- 7.a Load the data: using the variable names from nc_dump
- 7.b Load the data: using standard_names and coordinate attribute
- 7.c Load the data: using a dedicated function developed for grids
Create netCDF-CF of orthogonal x-y grid
example of how to make a netCDF file with CF conventions of a variable that is defined on a grid that is orthogonal in a x-y coordinate system. In this special case the dimensions coincide with the x-y coordinate axes. The associated lat-lon coordinate matrices are curvi-linear.
This case is described in: http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#grid-mappings-and-projections as "Horizontal Coordinate Reference Systems, Grid Mappings, and Projections".
An example of an orthogonal x,y grid is for instance a data product defined on an otrhogonal grid in a certain local geograhic projection by programs like arcGIS. For each vertex in this grid a the lat,lon values need to be calculated to project it on the globe (e.g. Google Earth).
^ latitude (degrees_north) ^ y(m) | x | | ncols / \ | ncols | / /\ \ coordinate +------+ | / /15\ \ transformation | x /10 \ \ <==============> +--------+ | <5 14\ \ | |05 10 15| + | \ 9 \ \ | | | | | \4 \ | | |04 09 14| | | \ \ | | | | | | )3 8 xx) | nrows | |03 08 xx| | nrows | / / | | | | | | /2 / | | |02 07 12| | | / 7 / / | | | | | <1 12/ / | |01 06 11| + | \6 / / | +--------+ | \11/ / | | \/ x | | | +----------------------> longitude +----------------------> x (degrees_east) (m)
Note that ncBrowse does not contain plot support for curvi-linear grids, so ncBrowse will display the same rectangular plot as for the netCDF file created by NC_CF_GRID_WRITE_LAT_LON_ORTHOGONAL_TUTORIAL, albeit with different axes annotations (col/row instead of lat/lon).
%See also: SNCTOOLS, NC_CF_GRID, NC_CF_GRID_WRITE, % NC_CF_GRID_WRITE_LAT_LON_ORTHOGONAL_TUTORIAL, % NC_CF_GRID_WRITE_LAT_LON_CURVILINEAR_TUTORIAL, % NC_CF_GRID_WRITE_X_Y_CURVILINEAR_TUTORIAL, % NC_CF_STATIONTIMESERIES % This tool is part of <a href="http://www.OpenEarth.eu">OpenEarthTools</a> under the <a href="http://www.gnu.org/licenses/gpl.html">GPL</a> license.
Define meta-info: global: x,y sticks > lat,lon matrices
OPT.title = ''; OPT.institution = ''; OPT.source = ''; OPT.history = ['tranformation to netCDF: $HeadURL: https://repos.deltares.nl/repos/OpenEarthTools/trunk/matlab/io/netcdf/nctools/nc_cf_grid_write_x_y_orthogonal_tutorial.m $']; OPT.references = ''; OPT.email = ''; OPT.comment = ''; OPT.version = ''; OPT.acknowledge =['These data can be used freely for research purposes provided that the following source is acknowledged: ',OPT.institution]; OPT.disclaimer = 'This data is made available 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.';
Define dimensions/coordinates
OPT.x = [400 500 600].*1e3; OPT.y = [5500:100:5900].*1e3; [x,y] = ndgrid(OPT.x,OPT.y); OPT.ncols = length(OPT.x); OPT.nrows = length(OPT.y); OPT.lat_type = 'single'; % 'single', 'double' for high-resolution data (eps 1m) OPT.lon_type = 'single'; % 'single', 'double' for high-resolution data (eps 1m) OPT.epsg.code = 32631; % epsg code of local projection OPT.wgs84.code = 4326; % epsg code of global grid % http://www.epsg-registry.org/ % in the case of a grid defined in a local x-y % projection, the properties of the grid in a WGS84 % lat,lon system do not have to be specified here, but % can be retrieved from the log of the coordinate % transformation carried out by convertCoordinates. % get (lat,lon) associated with each vertex (x,y), note order [OPT.lon,OPT.lat ... [OPT.lon,OPT.lat,log] = convertCoordinates(x,y,'CS1.code',OPT.epsg.code,'CS2.code',OPT.wgs84.code);
Define variable (define some data)
OPT.val = [1 2 3 4 5;6 7 8 9 10;11 12 nan 14 15]; % use ncols as 1st array dimension to get correct plot in ncBrowse (snctools swaps for us) OPT.varname = 'depth'; % free to choose: will appear in netCDF tree OPT.units = 'm'; % from UDunits package: http://www.unidata.ucar.edu/software/udunits/ OPT.long_name = 'bottom depth';% free to choose: will appear in plots OPT.standard_name = 'sea_floor_depth_below_geoid'; % or 'altitude' OPT.val_type = 'single'; % 'single' or 'double' OPT.fillvalue = nan;
1.a Create netCDF file
ncfile = fullfile(fileparts(mfilename('fullpath')),[mfilename,'.nc']); nc_create_empty (ncfile)
1.b Add overall meta info
http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#description-of-file-contents
nc_attput(ncfile, nc_global, 'title' , OPT.title); nc_attput(ncfile, nc_global, 'institution' , OPT.institution); nc_attput(ncfile, nc_global, 'source' , OPT.source); nc_attput(ncfile, nc_global, 'history' , OPT.history); nc_attput(ncfile, nc_global, 'references' , OPT.references); nc_attput(ncfile, nc_global, 'email' , OPT.email); nc_attput(ncfile, nc_global, 'comment' , OPT.comment); nc_attput(ncfile, nc_global, 'version' , OPT.version); nc_attput(ncfile, nc_global, 'Conventions' , 'CF-1.4'); nc_attput(ncfile, nc_global, 'CF:featureType', 'Grid'); % https://cf-pcmdi.llnl.gov/trac/wiki/PointObservationConventions nc_attput(ncfile, nc_global, 'terms_for_use' , OPT.acknowledge); nc_attput(ncfile, nc_global, 'disclaimer' , OPT.disclaimer);
2.a Create matrix span dimensions
http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#dimensions
nc_add_dimension(ncfile, 'x', OPT.ncols); % use this as 1st array dimension to get correct plot in ncBrowse (snctools swaps for us) nc_add_dimension(ncfile, 'y', OPT.nrows); % use this as 2nd array dimension to get correct plot in ncBrowse (snctools swaps for us) % You might insert a vector 'col' that runs max(x):-dx:min(x) to have % the arcGIS ASCII file approach of having upper-left corner of % the data matrix at index (1,1) rather than the default of having the % lower-left corner of the data matrix at index (1,1). % nc_add_dimension(ncfile, 'time', 1); % if you would like to include more instances of the same grid, % you can optionally use 'time' as a 3rd dimension. see % nc_cf_stationTimeSeries_write_tutorial for info on time.
3.a Create coordinate vectors: x and y
clear nc;ifld = 1; nc(ifld).Name = 'x'; % dimension 'x' is here filled with variable 'x' nc(ifld).Nctype = nc_type(OPT.lon_type); nc(ifld).Dimension = {'x'}; nc(ifld).Attribute( 1) = struct('Name', 'long_name' ,'Value', 'x Rijksdriehoek'); nc(ifld).Attribute(end+1) = struct('Name', 'units' ,'Value', 'm'); nc(ifld).Attribute(end+1) = struct('Name', 'standard_name' ,'Value', 'projection_x_coordinate'); % standard name nc(ifld).Attribute(end+1) = struct('Name', 'actual_range' ,'Value', [min(OPT.x(:)) max(OPT.x(:))]); nc(ifld).Attribute(end+1) = struct('Name', 'grid_mapping' ,'Value', 'epsg'); ifld = ifld + 1; nc(ifld).Name = 'y'; % dimension 'y' is here filled with variable 'y' nc(ifld).Nctype = nc_type(OPT.lat_type); nc(ifld).Dimension = {'y'}; nc(ifld).Attribute( 1) = struct('Name', 'long_name' ,'Value', 'y Rijksdriehoek'); nc(ifld).Attribute(end+1) = struct('Name', 'units' ,'Value', 'm'); nc(ifld).Attribute(end+1) = struct('Name', 'standard_name' ,'Value', 'projection_y_coordinate'); % standard name nc(ifld).Attribute(end+1) = struct('Name', 'actual_range' ,'Value', [min(OPT.y(:)) max(OPT.y(:))]); nc(ifld).Attribute(end+1) = struct('Name', 'grid_mapping' ,'Value', 'epsg');
3.b Create coordinate variables: coordinate system: epsg
http://www.epsg-registry.org/
ifld = ifld + 1;
nc(ifld).Name = 'epsg';
nc(ifld).Nctype = nc_int;
nc(ifld).Dimension = {};
nc(ifld).Attribute = nc_cf_grid_mapping(OPT.epsg.code);
var2evalstr(nc(ifld).Attribute)
OPT = debug: 0 grid_mapping_name: 'transverse_mercator' ans = variable1(1).Name = 'name'; variable1(1).Value = 'WGS 84 / UTM zone 31N'; variable1(2).Name = 'epsg'; variable1(2).Value = 32631; variable1(3).Name = 'epsg_name'; variable1(3).Value = 'Transverse Mercator'; variable1(4).Name = 'grid_mapping_name'; variable1(4).Value = 'transverse_mercator'; variable1(5).Name = 'semi_major_axis'; variable1(5).Value = 6378137; variable1(6).Name = 'semi_minor_axis'; variable1(6).Value = 6356752.3142; variable1(7).Name = 'inverse_flattening'; variable1(7).Value = 298.2572; variable1(8).Name = 'latitude_of_projection_origin'; variable1(8).Value = 0; variable1(9).Name = 'longitude_of_projection_origin'; variable1(9).Value = 3; variable1(10).Name = 'false_easting'; variable1(10).Value = 500000; variable1(11).Name = 'false_northing'; variable1(11).Value = 0; variable1(12).Name = 'scale_factor_at_projection_origin'; variable1(12).Value = 0.9996; variable1(13).Name = 'comment'; variable1(13).Value = 'value is equal to EPSG code';
3.c Create coordinate variables: longitude
http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#longitude-coordinate
ifld = ifld + 1; nc(ifld).Name = 'lon'; nc(ifld).Nctype = nc_type(OPT.lon_type); nc(ifld).Dimension = {'x','y'}; nc(ifld).Attribute( 1) = struct('Name', 'long_name' ,'Value', 'longitude'); nc(ifld).Attribute(end+1) = struct('Name', 'units' ,'Value', 'degrees_east'); nc(ifld).Attribute(end+1) = struct('Name', 'standard_name' ,'Value', 'longitude'); nc(ifld).Attribute(end+1) = struct('Name', 'actual_range' ,'Value', [min(OPT.lon(:)) max(OPT.lon(:))]); nc(ifld).Attribute(end+1) = struct('Name', 'coordinates' ,'Value', 'lat lon'); nc(ifld).Attribute(end+1) = struct('Name', 'grid_mapping' ,'Value', 'wgs84');
3.d Create coordinate variables: latitude
http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#latitude-coordinate
ifld = ifld + 1; nc(ifld).Name = 'lat'; nc(ifld).Nctype = nc_type(OPT.lat_type); nc(ifld).Dimension = {'x','y'}; nc(ifld).Attribute( 1) = struct('Name', 'long_name' ,'Value', 'latitude'); nc(ifld).Attribute(end+1) = struct('Name', 'units' ,'Value', 'degrees_north'); nc(ifld).Attribute(end+1) = struct('Name', 'standard_name' ,'Value', 'latitude'); nc(ifld).Attribute(end+1) = struct('Name', 'actual_range' ,'Value', [min(OPT.lat(:)) max(OPT.lat(:))]); nc(ifld).Attribute(end+1) = struct('Name', 'coordinates' ,'Value', 'lat lon'); nc(ifld).Attribute(end+1) = struct('Name', 'grid_mapping' ,'Value', 'wgs84');
3.e Create coordinate variables: coordinate system: WGS84 default
global ellispes: WGS 84, ED 50, INT 1924, ETRS 89 and the upcoming ETRS update etc. http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#grid-mappings-and-projections http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#appendix-grid-mappings
ifld = ifld + 1; nc(ifld).Name = 'wgs84'; % preferred nc(ifld).Nctype = nc_int; nc(ifld).Dimension = {}; nc(ifld).Attribute = nc_cf_grid_mapping(OPT.wgs84.code); var2evalstr(nc(ifld).Attribute)
ans = variable1(1).Name = 'name'; variable1(1).Value = 'WGS 84'; variable1(2).Name = 'epsg'; variable1(2).Value = 4326; variable1(3).Name = 'grid_mapping_name'; variable1(3).Value = 'latitude_longitude'; variable1(4).Name = 'semi_major_axis'; variable1(4).Value = 6378137; variable1(5).Name = 'semi_minor_axis'; variable1(5).Value = 6356752.3142; variable1(6).Name = 'inverse_flattening'; variable1(6).Value = 298.2572; variable1(7).Name = 'comment'; variable1(7).Value = 'value is equal to EPSG code';
4 Create dependent variable
http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#variables Parameters with standard names: http://cf-pcmdi.llnl.gov/documents/cf-standard-names/standard-name-table/current/
ifld = ifld + 1; nc(ifld).Name = OPT.varname; nc(ifld).Nctype = nc_type(OPT.val_type); nc(ifld).Dimension = {'x','y'}; % {'time','x','y'}; nc(ifld).Attribute( 1) = struct('Name', 'long_name' ,'Value', OPT.long_name ); nc(ifld).Attribute(end+1) = struct('Name', 'units' ,'Value', OPT.units ); nc(ifld).Attribute(end+1) = struct('Name', '_FillValue' ,'Value', OPT.fillvalue ); nc(ifld).Attribute(end+1) = struct('Name', 'actual_range' ,'Value', [min(OPT.val(:)) max(OPT.val(:))]); nc(ifld).Attribute(end+1) = struct('Name', 'coordinates' ,'Value', 'lat lon'); nc(ifld).Attribute(end+1) = struct('Name', 'grid_mapping' ,'Value', 'epsg'); if ~isempty(OPT.standard_name) nc(ifld).Attribute(end+1) = struct('Name', 'standard_name' ,'Value', OPT.standard_name); end
5.a Create all variables with attributes
for ifld=1:length(nc) nc_addvar(ncfile, nc(ifld)); end
5.b Fill all variables
nc_varput(ncfile, 'x' , OPT.x ); nc_varput(ncfile, 'y' , OPT.y ); nc_varput(ncfile, 'epsg' , OPT.epsg.code ); nc_varput(ncfile, 'lon' , OPT.lon ); nc_varput(ncfile, 'lat' , OPT.lat ); nc_varput(ncfile, 'wgs84' , OPT.wgs84.code); nc_varput(ncfile, OPT.varname , OPT.val );
6 Check file summary
nc_dump(ncfile); fid = fopen(fullfile(fileparts(mfilename('fullpath')),[mfilename,'.cdl']),'w'); nc_dump(ncfile,fid); fclose(fid)
netCDF F:\checkouts\OpenEarthTools\matlab\io\netcdf\nctools\nc_cf_grid_write_x_y_orthogonal_tutorial.nc { dimensions: x = 3 ; y = 5 ; variables: // Preference 'PRESERVE_FVD': false, // dimensions consistent with ncBrowse, not with native MATLAB netcdf package. single x(x), shape = [3] x:long_name = "x Rijksdriehoek" x:units = "m" x:standard_name = "projection_x_coordinate" x:actual_range = 400000 600000 x:grid_mapping = "epsg" single y(y), shape = [5] y:long_name = "y Rijksdriehoek" y:units = "m" y:standard_name = "projection_y_coordinate" y:actual_range = 5.5e+006 5.9e+006 y:grid_mapping = "epsg" int32 epsg([]), shape = [1] epsg:name = "WGS 84 / UTM zone 31N" epsg:epsg = 32631 epsg:epsg_name = "Transverse Mercator" epsg:grid_mapping_name = "transverse_mercator" epsg:semi_major_axis = 6.37814e+006 epsg:semi_minor_axis = 6.35675e+006 epsg:inverse_flattening = 298.257 epsg:latitude_of_projection_origin = 0 epsg:longitude_of_projection_origin = 3 epsg:false_easting = 500000 epsg:false_northing = 0 epsg:scale_factor_at_projection_origin = 0.9996 epsg:comment = "value is equal to EPSG code" single lon(x,y), shape = [3 5] lon:long_name = "longitude" lon:units = "degrees_east" lon:standard_name = "longitude" lon:actual_range = 1.50155 4.49845 lon:coordinates = "lat lon" lon:grid_mapping = "wgs84" single lat(x,y), shape = [3 5] lat:long_name = "latitude" lat:units = "degrees_north" lat:standard_name = "latitude" lat:actual_range = 49.6443 53.2493 lat:coordinates = "lat lon" lat:grid_mapping = "wgs84" int32 wgs84([]), shape = [1] wgs84:name = "WGS 84" wgs84:epsg = 4326 wgs84:grid_mapping_name = "latitude_longitude" wgs84:semi_major_axis = 6.37814e+006 wgs84:semi_minor_axis = 6.35675e+006 wgs84:inverse_flattening = 298.257 wgs84:comment = "value is equal to EPSG code" single depth(x,y), shape = [3 5] depth:long_name = "bottom depth" depth:units = "m" depth:_FillValue = NaN depth:actual_range = 1 15 depth:coordinates = "lat lon" depth:grid_mapping = "epsg" depth:standard_name = "sea_floor_depth_below_geoid" //global attributes: :title = "" :institution = "" :source = "" :history = "tranformation to netCDF: $HeadURL: https://repos.deltares.nl/repos/OpenEarthTools/trunk/matlab/io/netcdf/nctools/nc_cf_grid_write_x_y_orthogonal_tutorial.m $" :references = "" :email = "" :comment = "" :version = "" :Conventions = "CF-1.4" :CF:featureType = "Grid" :terms_for_use = "These data can be used freely for research purposes provided that the following source is acknowledged: " :disclaimer = "This data is made available 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." } ans = 0
7.a Load the data: using the variable names from nc_dump
Da.dep = nc_varget(ncfile,'depth'); Da.lat = nc_varget(ncfile,'lon'); Da.lon = nc_varget(ncfile,'lat')
Da = dep: [3x5 double] lat: [3x5 single] lon: [3x5 single] datenum: [6x1 double] var: [1 2 3 NaN 5 6]
7.b Load the data: using standard_names and coordinate attribute
depname = nc_varfind(ncfile,'attributename', 'standard_name', 'attributevalue', 'sea_floor_depth_below_geoid') Db.z = nc_varget(ncfile,depname); coords = nc_attget(ncfile,depname,'coordinates'); [ax1,coords] = strtok(coords); ax2 = strtok(coords); if strcmpi(nc_attget(ncfile,ax1,'standard_name'),'latitude') Db.lat = nc_varget(ncfile,ax1); Db.lon = nc_varget(ncfile,ax2) else Db.lat = nc_varget(ncfile,ax2); Db.lon = nc_varget(ncfile,ax1) end
depname = depth Db = z: [3x5 double] lat: [3x5 single] lon: [3x5 single] datenum: [6x1 double] var: [1 2 3 NaN 5 6]
7.c Load the data: using a dedicated function developed for grids
[Dc,Mc] = nc_cf_grid(ncfile,OPT.varname)
Dc = lon: [3x5 single] lat: [3x5 single] depth: [3x5 double] Mc = lon: [1x1 struct] lat: [1x1 struct] depth: [1x1 struct]