[Arjen Markus] (6 july 2009) NetCDF files are a popular way of distributing scientific data (results of numerical models or satellite data for instance). The library (found at for instance http://www.unidata.ucar.edu/software/netcdf/) is easy enough to use and there exist (at least?) two Tcl interfaces to it. Unfortunately, one has not been maintained over the years and the other is part of [NAP], a very nice extension, but it means you have to deal with NAP's data model. If you just want to store and retrieve data structured as a block, then the geographical coordinates may be a hindrance. So, here are the very humble beginnings of an extension that uses [Critcl] to read and write NetCDF files in a more basic way. I intend to add a higher-level API (and note: it does not yet retrieve the actual data ...) ---- ====== # bindnetcdf.tcl -- # Humble start of Tcl bindings for NetCDF # package require critcl critcl::config keepsrc 1 critcl::config force 1 # # NOTE: Windows-specific! # critcl::clibraries netcdf.lib #critcl::config I [pwd] set env(HOME) . critcl::ccode { #include #include "netcdf.h" } critcl::cproc nc_create {Tcl_Interp* ip char* fileName} ok { int ncid; int retval; retval = nc_create( fileName, NC_CLOBBER, &ncid ); if ( retval == 0 ) { Tcl_SetObjResult( ip, Tcl_NewIntObj(ncid) ); return TCL_OK; } else { Tcl_SetResult( ip, (char *) nc_strerror(retval), TCL_VOLATILE ); return TCL_ERROR; } } critcl::cproc nc_close {Tcl_Interp* ip int ncid} ok { int retval; retval = nc_close( ncid ); if ( retval == 0 ) { Tcl_SetObjResult( ip, Tcl_NewIntObj(ncid) ); return TCL_OK; } else { Tcl_SetResult( ip, (char *) nc_strerror(retval), TCL_VOLATILE ); return TCL_ERROR; } } critcl::cproc nc_enddef {Tcl_Interp* ip int ncid} ok { int retval; retval = nc_enddef( ncid ); if ( retval == 0 ) { return TCL_OK; } else { Tcl_SetResult( ip, (char *) nc_strerror(retval), TCL_VOLATILE ); return TCL_ERROR; } } critcl::cproc nc_def_dim {Tcl_Interp* ip int ncid char* dimName int dim} ok { int retval; int dimid; if ( dim <= 0 ) { dim = NC_UNLIMITED; } retval = nc_def_dim( ncid, dimName, dim, &dimid ); if ( retval == 0 ) { Tcl_SetObjResult( ip, Tcl_NewIntObj(dimid) ); return TCL_OK; } else { Tcl_SetResult( ip, (char *) nc_strerror(retval), TCL_VOLATILE ); return TCL_ERROR; } } critcl::cproc nc_def_var {Tcl_Interp* ip int ncid char* varName char* type Tcl_Obj* dims} ok { int retval; int typeCode; int varid; int dimid; int dimids[10]; int ndim; int i; Tcl_Obj *elemObj; /* Extract the IDs of the dimension variables */ retval = Tcl_ListObjLength( ip, dims, &ndim ); if ( retval != TCL_OK ) { return retval; } if ( ndim > 10 ) { Tcl_SetResult( ip, "Too many dimensions specified!", TCL_STATIC ); return TCL_ERROR; } for ( i = 0; i < ndim; i ++ ) { retval = Tcl_ListObjIndex( ip, dims, i, &elemObj ); if ( retval != TCL_OK ) { return retval; } retval = Tcl_GetIntFromObj( ip, elemObj, &dimids[i] ); if ( retval != TCL_OK ) { return retval; } } typeCode = -1; if ( strcmp( type, "int" ) == 0 ) typeCode = NC_INT; if ( strcmp( type, "long" ) == 0 ) typeCode = NC_LONG; if ( strcmp( type, "float" ) == 0 ) typeCode = NC_FLOAT; if ( strcmp( type, "double" ) == 0 ) typeCode = NC_DOUBLE; if ( typeCode == -1 ) { Tcl_SetResult( ip, "Invalid type specified!", TCL_STATIC ); return TCL_ERROR; } retval = nc_def_var( ncid, varName, typeCode, ndim, dimids, &varid ); if ( retval == 0 ) { Tcl_SetObjResult( ip, Tcl_NewIntObj(varid) ); return TCL_OK; } else { Tcl_SetResult( ip, (char *) nc_strerror(retval), TCL_VOLATILE ); return TCL_ERROR; } } critcl::cproc nc_put_att_text {Tcl_Interp* ip int ncid int varid char* attName char* attValue} ok { int retval; retval = nc_put_att_text( ncid, varid, attName, strlen(attValue), attValue); if ( retval == 0 ) { return TCL_OK; } else { Tcl_SetResult( ip, (char *) nc_strerror(retval), TCL_VOLATILE ); return TCL_ERROR; } return TCL_ERROR; } critcl::cproc nc_get_var {Tcl_Interp* ip int ncid char* varName} ok { /* TODO int retval; int dimid; retval = nc_get_dim( ncid, varName, &varid ); if ( retval == 0 ) { Tcl_SetObjResult( ip, Tcl_NewIntObj(varid) ); return TCL_OK; } else { Tcl_SetResult( ip, (char *) nc_strerror(retval), TCL_VOLATILE ); return TCL_ERROR; } */ return TCL_ERROR; } critcl::cproc nc_inq_varname {Tcl_Interp* ip int ncid int varid} ok { int retval; int dimid; char varName[NC_MAX_NAME+1]; retval = nc_inq_varname( ncid, varid, &varName ); if ( retval == 0 ) { Tcl_SetObjResult( ip, Tcl_NewStringObj(varName, -1) ); return TCL_OK; } else { Tcl_SetResult( ip, (char *) nc_strerror(retval), TCL_VOLATILE ); return TCL_ERROR; } return TCL_ERROR; } # main -- # Test the thing # set ncid [nc_create "somefile.nc"] set xdimid [nc_def_dim $ncid "X" 10] set ydimid [nc_def_dim $ncid "Y" 20] set xvarid [nc_def_var $ncid "X" float $xdimid] set yvarid [nc_def_var $ncid "Y" float $ydimid] set salin [nc_def_var $ncid "Salinity" float [list $xdimid $ydimid]] puts "$xdimid -- $ydimid" catch { nc_put_att_text $ncid $xdimid name "X-coordinate" } msg puts "NC: $msg" set err 0 set i 0 while { $err == 0 } { set err [catch { puts [nc_inq_varname $ncid $i] } msg] incr i } nc_close $ncid ====== ----- http://cnrit.tamu.edu/rsg/cdftcl/%|%cdftcl%|% is a Tcl/Tk interface for netCDF. It allows the use of "wildcards" (*) or ranges (1-4) in the subscript notation, and use of name references instead of variable IDs. <>Category Mathematics