Index: trunk/SRC/ReadWrite/ncdf_getatt.pro
===================================================================
--- trunk/SRC/ReadWrite/ncdf_getatt.pro	(revision 272)
+++ trunk/SRC/ReadWrite/ncdf_getatt.pro	(revision 272)
@@ -0,0 +1,116 @@
+;+
+;
+; @file_comments
+; Get variable attibutes from a NetCDF file
+;
+; @categories
+; Read NetCDF file
+;
+; @param fileid {in}{required}{type=salar string or long}
+; if fileid is a scalar string then it is the name of the file (with
+; the full path) to be opened (in that case, the file will be opened
+; and closed within ncdf_getatt).
+; if fileid is a scalar long then it is the id of the file return by a call
+; to ncdf_open outside of ncdf_getatt (in that case, the file will
+; NOT be opened and closed within ncdf_getatt)
+;
+; @param varid {in}{required}{type=salar string or long}
+; The netCDF variable ID, returned from a previous call to NCDF_VARDEF
+; or NCDF_VARID, or the name of the variable.
+;
+; @keyword add_offset 
+; Set this keyword to a named variable in which the value of
+; add_offset attribute is returned. Return 0. if this attribute
+; doesn't exist.
+;
+; @keyword scale_factor
+; Set this keyword to a named variable in which the value of
+; scale_factor attribute is returned. Return 0. if this attribute
+; doesn't exist.
+;
+; @keyword missing_value
+; Set this keyword to a named variable in which the value of
+; missing_value or _fillvalue attribute is returned. Return the scalar
+; 'no' if this attribute doesn't exist.
+;
+; @keyword units
+; Set this keyword to a named variable in which the value of
+; units attribute is returned. Return the empty string '' if this attribute
+; doesn't exist.
+;
+; @keyword calendar
+; Set this keyword to a named variable in which the value of
+; calendar attribute is returned. Return the string 'greg' if this attribute
+; doesn't exist.
+;
+; @examples
+; IDL> ncdf_getatt, cdfid, 'sst', add_offset = add_offset, scale_factor = scale_factor, units = units
+;
+; @history
+; August 2007: Sebastien Masson (smasson\@lodyc.jussieu.fr)
+;
+; @version
+; $Id$
+;
+;-
+
+PRO ncdf_getatt, fileid, varid, add_offset = add_offset, scale_factor = scale_factor, missing_value = missing_value, units = units, calendar = calendar
+;
+  compile_opt idl2, strictarrsubs
+;
+; should we open the file?
+  IF size(fileid, /type) EQ 7 THEN cdfid = ncdf_open(fileid) ELSE cdfid = fileid
+; default definitions
+  units = ''
+  add_offset = 0.
+  scale_factor = 1.
+  missing_value = 'no'
+  calendar = 'greg'
+; 
+  varinq = ncdf_varinq(cdfid, varid)
+  IF varinq.natts GT 0 THEN BEGIN
+    FOR a = 0, varinq.natts-1 DO BEGIN
+      attname = ncdf_attname(cdfid, varid, a)
+      CASE strlowcase(attname) OF
+        'units':BEGIN 
+          ncdf_attget, cdfid, varid, attname, tmp
+          units = strtrim(tmp, 2)
+        END
+        'calendar':BEGIN 
+          ncdf_attget, cdfid, varid, attname, tmp
+          tmp = strtrim(tmp, 2)
+          CASE tmp OF
+            'noleap':calendar = 'noleap'
+            '360d':calendar = '360d'
+            'greg':IF n_elements(calendar) EQ 0 THEN calendar = 'greg'
+            ELSE:BEGIN
+;            notused = report('Unknown calendar: '+tmp+', we use greg calendar.')
+              calendar = 'greg'
+            END
+          ENDCASE
+        END
+        'add_offset':ncdf_attget, cdfid, varid, attname, add_offset
+        'scale_factor':ncdf_attget, cdfid, varid, attname, scale_factor
+        '_fillvalue':ncdf_attget, cdfid, varid, attname, missing_value
+        'missing_value':ncdf_attget, cdfid, varid, attname, missing_value
+        ELSE:
+      ENDCASE
+    ENDFOR
+  ENDIF
+;
+  IF size(missing_value, /type) EQ 1 THEN BEGIN
+    missing_value = strlowcase(string(missing_value))
+    IF strmid(missing_value, 0, 1, /reverse_offset) EQ 'f' THEN $
+       missing_value = strmid(missing_value, 0, strlen(missing_value)-1)
+    IF isnumber(string(missing_value), tmp) EQ 1 THEN BEGIN 
+      missing_value = tmp
+    ENDIF ELSE BEGIN
+      ras = report('Warning: missing value is not a number: ' + missing_value)
+      missing_value = 'no'
+    ENDELSE
+  ENDIF
+
+  IF size(fileid, /type) EQ 7 THEN ncdf_close, cdfid
+  
+  return
+END 
Index: trunk/SRC/ReadWrite/ncdf_getmask.pro
===================================================================
--- trunk/SRC/ReadWrite/ncdf_getmask.pro	(revision 272)
+++ trunk/SRC/ReadWrite/ncdf_getmask.pro	(revision 272)
@@ -0,0 +1,138 @@
+;+
+;
+; @file_comments
+; get the land/sea mask array from a NetCDF file
+;
+; @categories
+; Read NetCDF file
+;
+; @param fileid {in}{required}{type=salar string or long}
+; if fileid is a scalar string then it is the name of the file (with
+; the full path) to be opened (in that case, the file will be opened
+; and closed within ncdf_getmask).
+; if fileid is a scalar then it is the id of the file return by a call
+; to ncdf_open outside of ncdf_getmask (in that case, the file will
+; NOT be opened and closed within ncdf_getmask)
+;
+; @keyword ADDSCL_BEFORE {default=0}{type=scalar: 0 or 1}
+; put 1 to apply add_offset ad scale factor on data before looking for
+; missing values when using USEASMASK keyword
+;
+; @keyword INVMASK {default=0}{type=scalar: 0 or 1}
+; Inverse the land/sea mask (that should have 0/1 values for land/sea): mask = 1-mask
+;
+; @keyword MASKNAME {type=string}
+; A string giving the name of the variable in the file
+; that contains the land/sea mask
+;
+; @keyword MISSING_VALUE {type=scalar}
+; To define (or redefine if the attribute is
+; already existing) the missing values used with USEASMASK
+; keyword
+;
+; @keyword USEASMASK {type=scalar string}
+; A string giving the name of the variable in the file
+; that will be used to build the land/sea mask. In this case the
+; mask is based on the first record (if record dimension
+; exists). The mask is build according to :
+;    1 the keyword missing_value if existing
+;    2 the attribute 'missing_value' if existing
+;    3 NaN values if existing
+;
+; @keyword 
+; _EXTRA to be able to call ncdf_getmask with _extra keyword
+;
+; @returns
+; the land/sea mask 2D or 3D array or -1 in case of error or mask absence
+;
+; @examples
+;
+; IDL> mask = ncdf_getmask('HadISST1_1m_187001_200702_sst_reg1m.nc',useasmask = 'sst', missing_value = -1.00000e+30)
+;
+; IDL> mask = ncdf_getmask('meshmaskORCA2.nc', maskname = 'tmask')
+;
+; @history
+; August 2007: Sebastien Masson (smasson\@lodyc.jussieu.fr)
+;
+; @version
+; $Id$
+;
+;-
+;
+FUNCTION ncdf_getmask, fileid, ADDSCL_BEFORE = addscl_before, MASKNAME = maskname, USEASMASK = useasmask $
+              , MISSING_VALUE = missing_value, _EXTRA = ex
+;
+  compile_opt idl2, strictarrsubs
+;
+  IF NOT (keyword_set(maskname) OR keyword_set(useasmask)) AND keyword_set(romsgrid) THEN maskname = 'mask_rho'
+  IF NOT (keyword_set(maskname) OR keyword_set(useasmask)) THEN return, -1
+;----------------------------------------------------------
+; should we open the file?
+  IF size(fileid, /type) EQ 7 THEN cdfid = ncdf_open(fileid) ELSE cdfid = fileid
+; what is inside the file
+  inq = ncdf_inquire(cdfid)
+; name of the variables
+  namevar = strarr(inq.nvars)
+  for varid = 0, inq.nvars-1 do begin
+    invar = ncdf_varinq(cdfid, varid)
+    namevar[varid] = strlowcase(invar.name)
+  ENDFOR
+;----------------------------------------------------------
+  CASE 1 OF
+    keyword_set(maskname):mskid = (where(namevar EQ strlowcase(maskname)))[0]
+    keyword_set(useasmask):mskid = (where(namevar EQ strlowcase(useasmask)))[0]
+  ENDCASE
+;
+  if mskid NE -1 THEN BEGIN
+    mskinq = ncdf_varinq(cdfid, mskid)
+; is the mask variable containing the record dimension?
+    withrcd = (where(mskinq.dim EQ inq.recdim))[0]
+    IF withrcd NE -1 THEN BEGIN
+; in order to read only the first record
+; we need to get the size of each dimension
+      count = replicate(1L, mskinq.ndims)
+      FOR d = 0, mskinq.ndims -1 DO BEGIN
+        IF d NE withrcd THEN BEGIN
+          ncdf_diminq, cdfid, mskinq.dim[d], name, size
+          count[d] = size
+        ENDIF
+      ENDFOR
+; read the variable for the first record
+      ncdf_varget, cdfid, mskid, mask, count = count
+    ENDIF ELSE ncdf_varget, cdfid, mskid, mask
+; check if we need to applay add_offset and scale factor
+    ncdf_getatt, cdfid, mskid, add_offset = add, scale_factor = scl, missing_value = miss
+    IF n_elements(missing_value) NE 0 THEN miss = missing_value
+
+    IF keyword_set(addscl_before) THEN BEGIN
+      IF scl NE 1 THEN mask = mask * scl
+      IF add NE 0 THEN mask = mask + add
+    ENDIF
+
+    IF keyword_set(useasmask)  THEN BEGIN
+      IF n_elements(miss) NE 0 THEN BEGIN
+; we have to take care of the float accuracy
+        CASE 1 OF
+          miss GE 1.e6:mask = mask LT (miss-10)
+          miss LE -1.e6:mask = mask GT (miss+10)
+          abs(miss) LE 1.e-6:mask = abs(mask) GT 1.e-6
+          ELSE:mask = mask NE miss
+        ENDCASE
+      ENDIF ELSE BEGIN
+        mask = finite(mask)
+        IF min(mask) EQ 1 THEN BEGIN
+          ras = report( 'missing or nan values not found...')
+          mask = -1
+        ENDIF
+      ENDELSE
+    ENDIF
+
+    mask = byte(round(mask))
+    if keyword_set(invmask) then mask = 1b-mask
+
+  ENDIF ELSE mask = -1
+
+  IF size(fileid, /type) EQ 7 THEN ncdf_close, cdfid
+
+  return, mask
+END
Index: trunk/SRC/ReadWrite/ncdf_gettime.pro
===================================================================
--- trunk/SRC/ReadWrite/ncdf_gettime.pro	(revision 272)
+++ trunk/SRC/ReadWrite/ncdf_gettime.pro	(revision 272)
@@ -0,0 +1,207 @@
+;+
+;
+; @file_comments
+; get the time axis from a netcdf_file and transforms it in
+; Julian days of IDL.
+;
+; @categories
+; Read NetCDF file
+;
+; @param filename {in}{required}{type=scalar string}
+; the name of the ncdf_file
+;
+; @param cdfid {in}{required}{type=scalar}
+; the ID of the ncdf_file, which is already open
+;
+; @keyword TIMEVAR {type=string}
+; It define the name of the variable that
+; contains the time axis. This keyword can be useful if there
+; is no unlimited dimension or if the time axis selected by default
+; (the first 1D array with unlimited dimension) is not the good one.
+;
+; @keyword CALLER {required}{type=scalar}
+; Used to specify the error messages. Give the name of the calling
+; procedure. It can be only 'read_ncdf' or 'scanfile'.
+;
+; @keyword ERR
+; Set this keyword to a named variable in which the value of the error
+; message will be returned
+;
+; @keyword _EXTRA
+; _EXTRA to be able to call ncdf_getmask with _extra keyword
+;
+; @returns
+; a double 1D array of IDL Julian days
+; In case of error return -1 if the time dimension was not found
+;               or return -jpt if it as been found that the time dimesion size is jpt
+;
+; @restrictions
+; the calendar variable must have the units attribute
+; following the syntax bellow:
+;
+; time_counter:units = "seconds since 0001-01-01 00:00:00" ;
+; time_counter:units = "hours since 0001-01-01 00:00:00" ;
+; time_counter:units = "days since 1979-01-01 00:00:00" ;
+; time_counter:units = "months since 1979-01-01 00:00:00" ;
+; time_counter:units = "years since 1979-01-01 00:00:00" ;
+;
+; @history
+; August 2007: Sebastien Masson (smasson\@lodyc.jussieu.fr)
+;
+; @version
+; $Id$
+;-
+;
+FUNCTION ncdf_gettime, filename, cdfid, TIMEVAR = timevar, CALLER = caller, ERR = err, _EXTRA = ex
+;
+  compile_opt idl2, strictarrsubs
+;
+  @cm_4cal                      ;   needed for key_caltype
+;
+  inq = ncdf_inquire(cdfid)
+;----------------------------------------------------
+;`find the variable containing the time axis
+;----------------------------------------------------
+; we get its name through the keyword timevar
+  IF keyword_set(timevar) THEN BEGIN
+    timeid = ncdf_varid(cdfid, timevar)
+    IF timeid EQ -1 THEN BEGIN   ;   the variable is not found
+      CASE caller OF
+        'read_ncdf':err = 'No variable ' + timevar + 'found in '+filename+'. Use the TIMESTEP keyword'
+        'scanfile':err = 'No variable ' + timevar + 'found in '+filename+'. We create a fake calendar'
+      ENDCASE
+      return, -1
+    ENDIF
+    timeinq = ncdf_varinq(cdfid, timeid)
+    inq.recdim = timeinq.dim[0]
+    ncdf_diminq, cdfid, inq.recdim, timedimname, jpt
+  ENDIF ELSE BEGIN
+; we try to find the time axis automatically
+; we look for the infinite dimension
+    IF inq.recdim EQ -1 THEN BEGIN
+      CASE caller OF
+        'read_ncdf':err = 'the file '+filename+' as no infinite dimension. !C Use TIMESTEP or TIMEVAR keyword'
+        'scanfile':err = 'the file '+filename+' as no infinite dimension. We create a fake calendar'
+      ENDCASE
+      return, -1
+    ENDIF
+    ncdf_diminq, cdfid, inq.recdim, timedimname, jpt
+; we look for the variable containing the time axis
+; we look for the first variable having for only dimension inq.recdim
+    timeid = 0
+    REPEAT BEGIN                ; As long as we have not find a variable having only one dimension: the infinite one
+      timeinq = ncdf_varinq(cdfid, timeid) ; that the variable contain.
+      timeid = timeid+1
+    ENDREP UNTIL (n_elements(timeinq.dim) EQ 1 AND timeinq.dim[0] EQ inq.recdim) $
+       OR timeid EQ inq.nvars+1
+    IF timeid EQ inq.nvars+1 THEN BEGIN
+      CASE caller OF
+        'read_ncdf':err = 'the file '+filename+' as no time axis variable. !C Use the TIMESTEP keyword'
+        'scanfile':err = 'the file '+fullname+' has no time axis.!C we create a fake calendar ...'
+      ENDCASE
+      return, -jpt
+    ENDIF
+    timeid = timeid-1
+  ENDELSE
+;----------------------------------------------------
+; look for attribute units and calendar to know how to compte the calendar
+;----------------------------------------------------
+; no attribute for time variable...
+  IF timeinq.natts EQ 0 then begin
+    CASE caller OF
+      'read_ncdf':err = 'the variable '+timeinq.name+' has no attribute.!C Use the TIMESTEP keyword'
+      'scanfile':err = 'the variable '+timeinq.name+' has no attribute.!C we create a fake calendar ...'
+    ENDCASE
+    return, -jpt
+  ENDIF
+; get attributes names
+  attnames = strarr(timeinq.natts)
+  for attiq = 0, timeinq.natts-1 do attnames[attiq] = strlowcase(ncdf_attname(cdfid, timeid, attiq))
+; do we find units attribute?
+  IF (where(attnames EQ 'units'))[0] EQ -1 then BEGIN
+    CASE caller OF
+      'read_ncdf':err = 'Attribute ''units'' not found for the variable '+timeinq.name+' !C Use the TIMESTEP keyword'
+      'scanfile':err = 'Attribute ''units'' not found for the variable '+timeinq.name+'!C we create a fake calendar ...'
+    ENDCASE
+    return, -jpt
+  ENDIF
+; Is attribute "calendar" existing?
+; If no, we suppose that the calendar is gregorian calendar
+;
+  if (where(attnames EQ 'calendar'))[0] NE -1 then BEGIN
+    ncdf_attget, cdfid, timeid, 'calendar', value
+    value = strlowcase(strtrim(value, 2))
+; unpade cm_4cal variable key_caltype (used by julday and caldat)
+    CASE value OF
+      'noleap':key_caltype = 'noleap'
+      '360d':key_caltype = '360d'
+      'greg':IF n_elements(key_caltype) EQ 0 THEN key_caltype = 'greg'
+      ELSE:BEGIN
+;            notused = report('Unknown calendar: '+value+', we use greg calendar.')
+        key_caltype = 'greg'
+      END
+    ENDCASE
+  ENDIF ELSE BEGIN
+;        notused = report('Unknown calendar, we use '+key_caltype+' calendar.')
+    IF n_elements(key_caltype) EQ 0 THEN key_caltype = 'greg'
+  ENDELSE
+;----------------------------------------------------
+; decode units attribute
+;----------------------------------------------------
+  ncdf_attget, cdfid, timeid, 'units', value
+;
+; time_counter:units = "seconds since 0001-01-01 00:00:00" ;
+; time_counter:units = "hours since 0001-01-01 00:00:00" ;
+; time_counter:units = "days since 1979-01-01 00:00:00" ;
+; time_counter:units = "months since 1979-01-01 00:00:00" ;
+; time_counter:units = "years since 1979-01-01 00:00:00" ;
+;
+; we decript the "units" attribute to find the time origin
+  value = strtrim(strcompress(string(value)), 2)
+  words = str_sep(value, ' ')
+  units = words[0]
+  units = strlowcase(units)
+  IF strpos(units, 's', strlen(units)-1) NE -1 THEN units = strmid(units, 0, strlen(units)-1)
+  IF strpos(units, 'julian_') NE -1 THEN units = strmid(units, 7)
+  IF units NE 'second' AND units NE 'hour' AND units NE 'day' $
+     AND units NE 'month' AND units NE 'year' THEN BEGIN
+    CASE caller OF
+      'read_ncdf':err = 'time units does not start with seconds/hours/days/months/years !C Use the TIMESTEP keyword'
+      'scanfile':err = 'time units does not start with seconds/hours/days/months/years !C we create a fake calendar ...'
+    ENDCASE
+    return, -jpt
+  ENDIF
+  IF stregex(value, '[^ ]* since ([0-9]){1,4}-([0-9]){1,2}-([0-9]){1,2}.*', /boolean) EQ 0 THEN BEGIN
+    CASE caller OF
+      'read_ncdf':err = 'attribute units of time has not the good format: [^ ]* since ([0-9]){1,4}-([0-9]){1,2}-([0-9]){1,2}.*!C Use the TIMESTEP keyword'
+      'scanfile':err = 'attribute units of time has not the good format: [^ ]* since ([0-9]){1,4}-([0-9]){1,2}-([0-9]){1,2}.*!C we create a fake calendar ...'
+    ENDCASE
+    return, -jpt
+  ENDIF
+  start = str_sep(words[2], '-')
+;----------------------------------------------------
+; compute time axis
+;----------------------------------------------------
+  ncdf_varget, cdfid, timeid, time
+  time = double(time)
+  case units of
+    'second':time = julday(start[1], start[2], start[0], 0, 0, 0)+time/86400.d
+    'hour':time = julday(start[1], start[2], start[0], 0, 0, 0)+time/24.d
+    'day':time = julday(start[1], start[2], start[0], 0, 0, 0)+time
+    'month':BEGIN
+      if total(fix(time) NE time) NE 0 then $ ; we switch to days with 30d/m
+         time = julday(start[1], start[2], start[0])+round(time*30) $
+      ELSE for t = 0, n_elements(time)-1 DO $
+         time[t] = julday(start[1]+time[t], start[2], start[0])
+    END
+    'year':BEGIN
+      if total(fix(time) NE time) NE 0 then $ ; we switch to days with 365d/y
+         time = julday(start[1], start[2], start[0])+round(time*365) $
+      ELSE for t = 0, n_elements(time)-1 do $
+         time[t] = julday(start[1], start[2], start[0]+time[t])
+    END
+  ENDCASE
+  time = double(time)
+;
+  return, time
+END
