1 | ; 1.11 01/10/07 |
---|
2 | ; |
---|
3 | ; Takes an IDL structure and writes to NetCDF much as netcdf_read |
---|
4 | ; but from the opposite direction. |
---|
5 | ; |
---|
6 | ; Dimension variables are identified as the first vector in the structure |
---|
7 | ; of a certain length, and linked to all following vectors and multi-D |
---|
8 | ; arrays with dimensions the same size. |
---|
9 | ; |
---|
10 | ; The approach is simple and powerful but with one big flaw - arrays |
---|
11 | ; with non-unique dimension sizes (e.g. fltarr[36,36,5] rather than |
---|
12 | ; fltarr[36,37,5]) cannot be written unless: |
---|
13 | ; |
---|
14 | ; a) A quick and dirty solution that breaks the NetCDF dimension |
---|
15 | ; links: the DIM_ALL keyword is used. |
---|
16 | ; |
---|
17 | ; b) A tedious but exact solution: the dimensions are explicilty |
---|
18 | ; specified with the DIMENSIONS keyword. |
---|
19 | ; |
---|
20 | ; It will be possible to actually define which are dimensions etc... |
---|
21 | ; ...in a later version... perhaps by defining "d_latitude" or "d_pressure" |
---|
22 | ; as indicating a dimension? Or if this is not flexible enough |
---|
23 | ; just write your own netcdf file following the Chapter 6 of the IDL |
---|
24 | ; Scientific Data Formats manual |
---|
25 | ; |
---|
26 | ; Parameters |
---|
27 | ; ---------- |
---|
28 | ; |
---|
29 | ; I: file string containing filename and path to be written |
---|
30 | ; I: field IDL structure to be written to file, arranged as |
---|
31 | ; in the example below |
---|
32 | ; |
---|
33 | ; Keywords |
---|
34 | ; -------- |
---|
35 | ; |
---|
36 | ; CLOBBER - overwrite any existing file (otherwise, we fail with error |
---|
37 | ; status if there's an exisitng file there) |
---|
38 | ; |
---|
39 | ; DIM_ALL - treat all vectors as dimensions. This is needed in order to |
---|
40 | ; write arrays non-unique dimension sizes, e.g. fltarr[36,36,5] |
---|
41 | ; |
---|
42 | ; DIMENSIONS - specify the exact dimensions and links. See below for |
---|
43 | ; an example. |
---|
44 | ; |
---|
45 | ; ATTRIBUTES - pass in a structure containing the attributes for each |
---|
46 | ; dimension and variable, in the order they are found in the data |
---|
47 | ; structure we are writing to file. The structure should contain a |
---|
48 | ; strarr for each attribute. The attribute name when written to |
---|
49 | ; file will be the variable name in the struture, in lowercase. |
---|
50 | ; |
---|
51 | ; Example |
---|
52 | ; ------- |
---|
53 | ; |
---|
54 | ; To add "units" and "long_name" attributes to each variable |
---|
55 | ; for a partial compatability with the NetCDF standard, e.g. when |
---|
56 | ; writing the 3D array/structure ozone: |
---|
57 | ; |
---|
58 | ; ozone = {lon:fltarr(96),lat:fltarr(73),pressure:fltarr(50),$ |
---|
59 | ; ozone:fltarr(96,73,50)} |
---|
60 | ; |
---|
61 | ; attributes = {units:strarr(4),long_name:strarr(4)} |
---|
62 | ; attributes.units = ['degrees_east','degrees_north','Pa','kg kg-1'] |
---|
63 | ; attributes.long_name = ['Longitude','Latitude','Pressure',$ |
---|
64 | ; 'Ozone'] |
---|
65 | ; |
---|
66 | ; netcdfwrite, 'filename', ozone, attributes=attributes |
---|
67 | ; |
---|
68 | ; On the unix command line "ncdump -h filename" produces: |
---|
69 | ; |
---|
70 | ; netcdf filename { |
---|
71 | ; dimensions: |
---|
72 | ; LON = 96 ; |
---|
73 | ; LAT = 73 ; |
---|
74 | ; PRESSURE = 50 ; |
---|
75 | ; variables: |
---|
76 | ; float LON(LON) ; |
---|
77 | ; LON:units = "degrees_east" ; |
---|
78 | ; LON:long_name = "Longitude" ; |
---|
79 | ; float LAT(LAT) ; |
---|
80 | ; LAT:units = "degrees_north" ; |
---|
81 | ; LAT:long_name = "Latitude" ; |
---|
82 | ; float PRESSURE(PRESSURE) ; |
---|
83 | ; PRESSURE:units = "Pa" ; |
---|
84 | ; PRESSURE:long_name = "Pressure" ; |
---|
85 | ; float OZONE(PRESSURE, LAT, LON) ; |
---|
86 | ; OZONE:units = "kg kg-1" ; |
---|
87 | ; OZONE:long_name = "Ozone" ; |
---|
88 | ; } |
---|
89 | ; |
---|
90 | ; Note that NCDUMP reverses the order of the dimensions in multi- |
---|
91 | ; dimensional arrays compared to IDL. |
---|
92 | ; |
---|
93 | ; To explicitly specify the dimensions and linkages (not needed in most |
---|
94 | ; cases including this example: it's only needed if the rule of matching |
---|
95 | ; dimensions by length breaks down because there are two dimensions of the |
---|
96 | ; same length): |
---|
97 | ; |
---|
98 | ; dimensions = {isdim:intarr(4), links:intarr(5,4)} |
---|
99 | ; dimensions.isdim = [1,1,1,0] ; (1=dimension, 0=variable) |
---|
100 | ; dimensions.links = [[-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1],$ |
---|
101 | ; [-1,-1,-1,-1,-1],[0,1,2,-1,-1]] |
---|
102 | ; |
---|
103 | ; netcdfwrite, 'filename', ozone, attributes=attributes, $ |
---|
104 | ; dimensions=dimensions |
---|
105 | ; |
---|
106 | ; |
---|
107 | ; |
---|
108 | ; AJG 22/5/2003 |
---|
109 | ; |
---|
110 | |
---|
111 | ; ------------------------------------------------------------------ |
---|
112 | ; |
---|
113 | ; Add a non-string variable to the NetCDF file |
---|
114 | ; |
---|
115 | ; INPUTS |
---|
116 | ; |
---|
117 | ; var_type - IDL type code - see IDL help for SIZE function |
---|
118 | ; ncid, name, dim_ids - parameters passed to ncdf_vardef (see IDL help here too) |
---|
119 | ; |
---|
120 | ; RETURNS |
---|
121 | ; |
---|
122 | ; NetCDF variable ID |
---|
123 | ; |
---|
124 | function add_var, var_type, ncid, name, dim_ids |
---|
125 | |
---|
126 | case var_type of |
---|
127 | ; byte |
---|
128 | 1: var_id = ncdf_vardef(ncid, name, dim_ids, /BYTE) |
---|
129 | |
---|
130 | ; int |
---|
131 | 2: var_id = ncdf_vardef(ncid, name, dim_ids, /SHORT) |
---|
132 | |
---|
133 | ; long |
---|
134 | 3: var_id = ncdf_vardef(ncid, name, dim_ids, /LONG) |
---|
135 | |
---|
136 | ; float |
---|
137 | 4: var_id = ncdf_vardef(ncid, name, dim_ids, /FLOAT) |
---|
138 | |
---|
139 | ; double |
---|
140 | 5: var_id = ncdf_vardef(ncid, name, dim_ids, /DOUBLE) |
---|
141 | |
---|
142 | ; catch all (will probably catch us out some-day...) |
---|
143 | else: var_id = ncdf_vardef(ncid, name, dim_ids, /FLOAT) |
---|
144 | endcase |
---|
145 | |
---|
146 | return, var_id |
---|
147 | |
---|
148 | end |
---|
149 | |
---|
150 | ; ------------------------------------------------------------------ |
---|
151 | ; |
---|
152 | ; Main function |
---|
153 | ; |
---|
154 | |
---|
155 | pro netcdfwrite, file, field, clobber=clobber, dim_all=dim_all, $ |
---|
156 | attributes=attributes, dimensions=dimensions |
---|
157 | |
---|
158 | on_error, 2 ; on error, return to caller (could be improved |
---|
159 | ; at a later date to catch IO errors and close the |
---|
160 | ; file correctly). |
---|
161 | |
---|
162 | if ~keyword_set(clobber) then clobber = 0 |
---|
163 | |
---|
164 | tag_names = tag_names(field) |
---|
165 | nTags = N_TAGS(field) |
---|
166 | |
---|
167 | if keyword_set(attributes) then begin |
---|
168 | ; Attributes are being supplied. Check they match the |
---|
169 | ; data structure also supplied. |
---|
170 | att_names = tag_names(attributes) |
---|
171 | nAtts = n_tags(attributes) |
---|
172 | nInAtt = lonarr(nAtts) |
---|
173 | for i=0,nAtts-1 do nInAtt[i] = n_elements(attributes.(i)) |
---|
174 | if ~array_equal(nInAtt,replicate(nTags,nAtts)) then message, $ |
---|
175 | 'Each attribute must have a member for each data structure element' |
---|
176 | endif |
---|
177 | |
---|
178 | ncid = ncdf_create(file, clobber = clobber) |
---|
179 | |
---|
180 | ; Run through all data "tags" to identify "dimensions" and |
---|
181 | ; "variables". For this to work, the data structure must always |
---|
182 | ; have dimensions before normal variables |
---|
183 | struct_dims, field, dimension_sizes, is_string_max, var_type, $ |
---|
184 | nDimensions, dim_num, iDimLink, dim_all=dim_all, dimensions=dimensions |
---|
185 | |
---|
186 | dim_ids = intarr(nTags) |
---|
187 | var_ids = intarr(nTags) |
---|
188 | string_id = 0 ; a unique ID for any hidden string dimensions |
---|
189 | |
---|
190 | ; initialise file, defining the dimensions and variables |
---|
191 | ncdf_control, ncid, /fill |
---|
192 | for iTag=0, nTags-1 do begin |
---|
193 | |
---|
194 | if dim_num[iTag] eq 0 then begin |
---|
195 | |
---|
196 | ; Each dimension will have an associated variable |
---|
197 | ; containing the values at each point |
---|
198 | dim_ids[iTag] = ncdf_dimdef(ncid, tag_names[iTag], $ |
---|
199 | dimension_sizes[iTag]) |
---|
200 | |
---|
201 | var_ids[iTag] = add_var(var_type[iTag], ncid, tag_names[iTag], $ |
---|
202 | dim_ids[iTag]) |
---|
203 | |
---|
204 | endif else begin |
---|
205 | if is_string_max[iTag] eq 0 then begin |
---|
206 | |
---|
207 | var_ids[iTag] = add_var(var_type[iTag], ncid, tag_names[iTag], $ |
---|
208 | dim_ids[reform(iDimlink[iTag,0:dim_num[iTag]-1])]) |
---|
209 | |
---|
210 | endif else begin |
---|
211 | |
---|
212 | ; Deal with the string array special case |
---|
213 | |
---|
214 | ; * Note that a string variable will gain an extra, hidden |
---|
215 | ; dimension when we write it to file because of NetCDF |
---|
216 | ; limitations. |
---|
217 | |
---|
218 | ; Create a new dimension for the array of characters which forms |
---|
219 | ; a string, with its length being that of the longest string |
---|
220 | ; in the original array of strings. Don't bother defining |
---|
221 | ; a variable for the dimension. |
---|
222 | string_id = string_id+1 |
---|
223 | string_dim = ncdf_dimdef(ncid, $ |
---|
224 | 'STRING'+string(format='(i0)', string_id), $ |
---|
225 | is_string_max[iTag]) |
---|
226 | |
---|
227 | ; Combine the "IDL" dimensions with the extra hidden |
---|
228 | ; string dimension (which must be the first, "fastest varying") |
---|
229 | string_dim_ids = $ |
---|
230 | [string_dim, dim_ids[reform(iDimlink[iTag,0:dim_num[iTag]-1])]] |
---|
231 | var_ids[iTag] = ncdf_vardef(ncid, tag_names[iTag], $ |
---|
232 | string_dim_ids, /CHAR) |
---|
233 | |
---|
234 | endelse |
---|
235 | endelse |
---|
236 | |
---|
237 | ; Write any associated atributes |
---|
238 | if keyword_set(attributes) then begin |
---|
239 | for iAtt = 0, nAtts-1 do begin |
---|
240 | ncdf_attput, ncid, var_ids[iTag], strlowcase(att_names[iAtt]), $ |
---|
241 | attributes.(iAtt)[iTag] |
---|
242 | endfor |
---|
243 | endif |
---|
244 | |
---|
245 | endfor |
---|
246 | ncdf_control, ncid, /endef |
---|
247 | |
---|
248 | ; write the data |
---|
249 | for iTag=0, nTags-1 do begin |
---|
250 | |
---|
251 | ncdf_varput, ncid, var_ids[iTag], field.(iTag) |
---|
252 | |
---|
253 | endfor |
---|
254 | |
---|
255 | ; close the file |
---|
256 | ncdf_close, ncid |
---|
257 | |
---|
258 | end |
---|