npp/01.read_81.ncl.x
changeset 1 4be95183fbcd
equal deleted inserted replaced
-1:000000000000 0:b65f84181194
       
     1 ;----------------------------------------------------------------------
       
     2 ; This example reads an ASCII file that is formatted a specific way, and
       
     3 ; writes out the results to a netCDF file.
       
     4 ;
       
     5 ; The first line in the ASCII file must be a header, with each field
       
     6 ; separated by a single character delimiter (like a ","). The rest of
       
     7 ; the file must be such that each row contains all fields, each
       
     8 ; separated by the designated delimiter.
       
     9 ;
       
    10 ; The fields can be integer, float, double, or string.
       
    11 ;----------------------------------------------------------------------
       
    12 
       
    13 ;----------------------------------------------------------------------
       
    14 ; This function returns the index locations of the given delimiter
       
    15 ; in a row or several rows of strings.
       
    16 ;----------------------------------------------------------------------
       
    17 function delim_indices(strings,nfields,delimiter)
       
    18 local cstrings, cdelim
       
    19 begin
       
    20   nrows = dimsizes(strings)
       
    21 ;
       
    22 ; Handle special case if we only have one string. Make sure it
       
    23 ; is put into a 2D array.
       
    24 ;
       
    25   if(nrows.eq.1) then
       
    26     cstrings = new((/1,strlen(strings)+1/),character)
       
    27   end if
       
    28 
       
    29   cstrings = stringtochar(strings)        ; Convert to characters.
       
    30   cdelim   = stringtochar(delimiter)      ; Convert delimiter to character.
       
    31 ;
       
    32 ; Som error checking here. Make sure delimiter is one character.
       
    33 ;
       
    34   nc   = dimsizes(cdelim)
       
    35   rank = dimsizes(nc)
       
    36   if(rank.ne.1.or.(rank.eq.1.and.nc.ne.2)) then
       
    37     print("delim_indices: fatal: the delimiter you've selected")
       
    38     print("must be a single character. Can't continue.")
       
    39     exit
       
    40   end if
       
    41 
       
    42 ;
       
    43 ; Create array to hold indices of delimiter locations, and then loop
       
    44 ; through each row and find all the delimiters. Make sure each row has
       
    45 ; the correct number of delimiters.
       
    46 ;
       
    47   ndelims  = nfields-1
       
    48   cindices = new((/nrows,ndelims/),integer)
       
    49   do i = 0, nrows-1
       
    50     ii = ind(cstrings(i,:).eq.cdelim(0))
       
    51 ;
       
    52 ; Make sure there were delimiters on this row. If not, we just quit.
       
    53 ; This could probably be modified to do this more gracefully.
       
    54 ;
       
    55     if(any(ismissing(ii))) then
       
    56       print("delim_indices: fatal: I didn't find any delimiters")
       
    57       print("('" + delimiter + "') on row " + i + ". Can't continue.")
       
    58       exit
       
    59     end if
       
    60     if(dimsizes(ii).ne.ndelims) then
       
    61       print("delim_indices: fatal: I expected to find " + ndelims)
       
    62       print("delimiters on row " + i + ". Instead, I found " + dimsizes(ii) + ".")
       
    63       print("Can't continue.")
       
    64       exit
       
    65     end if
       
    66 
       
    67     cindices(i,:) = ii
       
    68 
       
    69     delete(ii)            ; For next time through loop
       
    70   end do
       
    71 
       
    72   return(cindices)
       
    73 end
       
    74 
       
    75 ;----------------------------------------------------------------------
       
    76 ; This function reads in a particular field from a string array,
       
    77 ; given the field number to read (fields start at 1 and go to nfield),
       
    78 ; and the indices of the delimiters.
       
    79 ;
       
    80 ; It returns either an integer, float, double, or a string,
       
    81 ; depending on the input flag "return_type".
       
    82 ;----------------------------------------------------------------------
       
    83 function read_field(strings,ifield,indices,return_type)
       
    84 local nstring, cstrings, nf, tmp_str
       
    85 begin
       
    86   nrows = dimsizes(strings)
       
    87 ;
       
    88 ; Handle special case if we only have one string. Make sure it
       
    89 ; is put into a 2D array.
       
    90 ;
       
    91   if(nrows.eq.1) then
       
    92     cstrings = new((/1,strlen(strings)+1/),character)
       
    93   end if
       
    94 
       
    95   cstrings = stringtochar(strings)
       
    96   nf       = dimsizes(indices(0,:))+1     ; indices is nrows x (nfields-1)
       
    97 
       
    98 ;
       
    99 ; Error checking. Make sure user has entered a valid field.
       
   100 ;
       
   101   if(ifield.le.0.or.ifield.gt.nf) then
       
   102     print("read_field: fatal: you've selected a field that is")
       
   103     print("out-of-range of the number of fields that you have (" + nf + ").")
       
   104     exit
       
   105   end if
       
   106 
       
   107 ;
       
   108 ; Set up array to return.
       
   109 ;
       
   110   return_array = new(nrows,return_type)
       
   111 
       
   112   do i = 0,nrows-1
       
   113 ;
       
   114 ; Special case of first field in row.
       
   115 ;
       
   116     if(ifield.eq.1) then
       
   117       ibeg = 0
       
   118       iend = indices(i,ifield-1)-1
       
   119     else
       
   120 ;
       
   121 ; Special case of first field in row.
       
   122 ;
       
   123       if(ifield.eq.nf) then
       
   124         ibeg = indices(i,ifield-2)+1
       
   125         iend = dimsizes(cstrings(i,:))-1
       
   126 ;
       
   127 ; Any field between first and last field.
       
   128 ;
       
   129       else
       
   130         ibeg = indices(i,ifield-2)+1
       
   131         iend = indices(i,ifield-1)-1
       
   132       end if  
       
   133     end if
       
   134 ;
       
   135 ; Here's the code that pulls off the correct string, and converts it
       
   136 ; to float if desired.
       
   137 ;
       
   138     if(return_type.eq."integer") then
       
   139       return_array(i) = stringtointeger(chartostring(cstrings(i,ibeg:iend)))
       
   140     end if
       
   141     if(return_type.eq."float") then
       
   142       return_array(i) = stringtofloat(chartostring(cstrings(i,ibeg:iend)))
       
   143     end if
       
   144     if(return_type.eq."double") then
       
   145       return_array(i) = stringtodouble(chartostring(cstrings(i,ibeg:iend)))
       
   146     end if
       
   147     if(return_type.eq."string") then
       
   148       return_array(i) = chartostring(cstrings(i,ibeg:iend))
       
   149     end if
       
   150   end do
       
   151 
       
   152   return(return_array)
       
   153 end
       
   154 
       
   155 ;----------------------------------------------------------------------
       
   156 ; Main code.
       
   157 ;----------------------------------------------------------------------
       
   158 begin
       
   159 ;
       
   160 ; Set up defaults here.  We have to hard-code the field types,
       
   161 ; because we can't really tell otherwise.
       
   162 
       
   163   filename  = "data.81"                 ; ASCII" file to read.
       
   164   cdf_file  = filename + ".nc"          ; netCDF file to write.
       
   165   nfields   = 22                        ; # of fields
       
   166   delimiter = ","                       ; field delimiter
       
   167 ;
       
   168 ; In this case, fields #6-#8 are strings, fields #2, #3, and #11
       
   169 ; are float, and the rest of the fields are integers.
       
   170 ;
       
   171   var_types      = new(nfields,string)
       
   172   var_types      = "integer"    ; Most are ints.
       
   173   var_types(5:7) = "string"     ; corresponds to fields 6-8
       
   174   var_types(1:2) = "float"
       
   175   var_types(10)  = "float"
       
   176 
       
   177   if(isfilepresent(cdf_file))
       
   178     print("Warning: '" + cdf_file + "' exists. Will remove it.")
       
   179     system("/bin/rm " + cdf_file)
       
   180   end if
       
   181 
       
   182 ;
       
   183 ; Read in data as strings. This will create a string array that has the
       
   184 ; same number of strings as there are rows in the file. We will then need
       
   185 ; to parse each string later.
       
   186 ;
       
   187   read_data = asciiread(filename,-1,"string")
       
   188   header    = read_data(0)        ; Header. Use for variable names.
       
   189   data      = read_data(1:)       ; Get rid of first line which is a header.
       
   190   nrows     = dimsizes(data)      ; Number of rows.
       
   191 
       
   192 ;
       
   193 ; Read in locations of delimiters in each string row.
       
   194 ;
       
   195   hindices = delim_indices(header,nfields,delimiter)   ; header row
       
   196   dindices = delim_indices(data,nfields,delimiter)     ; rest of file
       
   197 
       
   198 ;
       
   199 ; Read in the field names which will become variable names on
       
   200 ; the netCDF file.
       
   201 ;
       
   202   var_names = new(nfields,string)
       
   203 
       
   204   do i=0,nfields-1
       
   205     var_names(i) = read_field(header,i+1,hindices,"string")
       
   206   end do
       
   207 ;
       
   208 ; Write out this netCDF file efficiently so it will be faster.
       
   209 ; Try to predefine everything before you write to it.
       
   210 ; 
       
   211   f = addfile(cdf_file,"c")
       
   212   setfileoption(f,"DefineMode",True)       ; Enter predefine phase.
       
   213 
       
   214 ;
       
   215 ; Write global attributes to file. It's okay to do this before 
       
   216 ; predefining the file's variables. We are still in "define" mode.
       
   217 ;
       
   218   fAtt               = True
       
   219   fAtt@description   = "Data read in from '" + filename + "' ASCII file."
       
   220   fAtt@creation_date = systemfunc ("date")        
       
   221   fileattdef( f, fAtt )        
       
   222 
       
   223 ;
       
   224 ; Write dimension name. There's only one here.
       
   225 ;
       
   226   filedimdef(f,"nvalues",-1,True)
       
   227 
       
   228 ;
       
   229 ; Define each variable on the file, giving it the dimension name
       
   230 ; we just created above.
       
   231 ;
       
   232 ; Don't deal with variables that are of type string.
       
   233 ;
       
   234   do i=0,nfields-1
       
   235     if(var_types(i).ne."string") then
       
   236       filevardef(f, var_names(i), var_types(i), "nvalues")
       
   237     end if
       
   238   end do
       
   239 
       
   240 ;
       
   241 ; Loop through each field, read the values for that field, print 
       
   242 ; information about the variable, and then write it to the netCDF
       
   243 ; file.
       
   244 ;
       
   245   do i=0,nfields-1
       
   246     ifield = i+1           ; Fields start at #1, not #0.
       
   247 ;
       
   248 ; You can't write strings to a netCDF file, so skip the string fields.
       
   249 ;
       
   250     if(var_types(i).ne."string") then
       
   251       tmp_data = read_field(data,ifield,dindices,var_types(i))
       
   252 ;
       
   253 ; Print some info about the variable.
       
   254 ;
       
   255       print("")
       
   256       print("Writing variable " + var_names(i) + " (field #" + ifield + ").")
       
   257       print("Type is " + var_types(i) + ", min/max = " + min(tmp_data) + \
       
   258             "/" + max(tmp_data))
       
   259 
       
   260       if(any(ismissing(tmp_data))) then
       
   261         print("This variable does contain missing values.")
       
   262       else
       
   263         print("This variable doesn't contain missing values.")
       
   264       end if
       
   265 
       
   266       f->$var_names(i)$ = tmp_data       ; Write to netCDF file.
       
   267 
       
   268       delete(tmp_data)                   ; Delete for next round.
       
   269     end if
       
   270   end do
       
   271 end