1 | #pragma rtGlobals=3 // Use modern global access method. |
---|
2 | |
---|
3 | #include <HDF5 Browser> |
---|
4 | |
---|
5 | // |
---|
6 | // !!! search for "SRK" to see what I have modified to work with NCNR files |
---|
7 | // -- these changes may only be for testing, and may need to be removed in the final version!!! |
---|
8 | // |
---|
9 | // |
---|
10 | // |
---|
11 | |
---|
12 | |
---|
13 | // requires the Wavemetrics "HDF5.xop" to be installed for IgorPro |
---|
14 | |
---|
15 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
---|
16 | // $Id: HDF5gateway.ipf 768 2012-11-26 04:48:16Z svnsmang $ |
---|
17 | // This file is part of a project hosted at the Advanced Photon Source. |
---|
18 | // Access all the source files and data files by checking out the project here: |
---|
19 | // svn co https://subversion.xray.aps.anl.gov/small_angle/hdf5gateway/trunk hdf5gateway |
---|
20 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
---|
21 | // The documentation is written in restructured text for use by Sphinx. |
---|
22 | // A python program will read this file and grab all the text between lines |
---|
23 | // beginning with "//@+" and "//@-" that begin with "//\t" and use them for the Sphinx documentation. |
---|
24 | // The tables here have been adjusted for a fixed width font. Don't "fix" them in Igor! |
---|
25 | |
---|
26 | //@+ |
---|
27 | // ============================================================ |
---|
28 | // HDF5gateway: HDF5 File I/O Support |
---|
29 | // ============================================================ |
---|
30 | // |
---|
31 | // Version: 1.0 |
---|
32 | // |
---|
33 | // HDF5gateway makes it easy to read |
---|
34 | // a HDF5 file into an IgorPro folder, |
---|
35 | // including group and dataset attributes, |
---|
36 | // such as a NeXus data file, |
---|
37 | // modify it, and then write it back out. |
---|
38 | // |
---|
39 | // .. index:: goal |
---|
40 | // |
---|
41 | // The goal was to make it easy to read a HDF5 file into an IgorPro folder, |
---|
42 | // including group and dataset attributes, |
---|
43 | // such as a NeXus data file, |
---|
44 | // modify it, and then write it back out. |
---|
45 | // This file provides functions to do just that. |
---|
46 | // |
---|
47 | // .. index:: functions; public |
---|
48 | // |
---|
49 | // Starting with utilities provided in the *HDF5 Browser* package, this file provides |
---|
50 | // these public functions: |
---|
51 | // |
---|
52 | // * :ref:`H5GW_ReadHDF5` |
---|
53 | // * :ref:`H5GW_WriteHDF5` |
---|
54 | // * :ref:`H5GW_ValidateFolder` |
---|
55 | // |
---|
56 | // and this function which is useful only for testing and development: |
---|
57 | // |
---|
58 | // * :ref:`H5GW_TestSuite` |
---|
59 | // |
---|
60 | // Help is provided with each of these functions to indicate their usage. |
---|
61 | // |
---|
62 | // .. index:: read |
---|
63 | // |
---|
64 | // Reading |
---|
65 | // =========== |
---|
66 | // |
---|
67 | // An HDF5 file is read into an IgorPro data folder in these steps: |
---|
68 | // |
---|
69 | // #. The groups and datasets are read and stored into an IgorPro folder. |
---|
70 | // #. Any attributes of these groups and datasets are read and assigned to IgorPro objects. |
---|
71 | // |
---|
72 | // .. index:: home |
---|
73 | // |
---|
74 | // The data file is expected to be in the *home* folder (the folder specified by IgorPro's *home* path), |
---|
75 | // or relative to that folder, or given by an absolute path name. |
---|
76 | // |
---|
77 | // .. index:: write, HDF5___xref |
---|
78 | // |
---|
79 | // Writing |
---|
80 | // ================= |
---|
81 | // |
---|
82 | // An IgorPro data folder is written to an HDF5 file in these steps: |
---|
83 | // |
---|
84 | // #. The IgorPro folder is validated for correct structure. |
---|
85 | // #. The objects in the *HDF5___xref* text wave are written to the HDF5 file. |
---|
86 | // #. Any folder attributes or wave notes are written to the corresponding HDF5 data path. |
---|
87 | // |
---|
88 | // The data file is expected to be in the *home* folder (the folder specified by IgorPro's *home* path), |
---|
89 | // or relative to that folder, or given by an absolute path name. |
---|
90 | // |
---|
91 | // .. index:: validate |
---|
92 | // |
---|
93 | // Validating |
---|
94 | // ================= |
---|
95 | // |
---|
96 | // Call :ref:`H5GW_ValidateFolder` to test if the |
---|
97 | // *parentFolder* in the IgorPro Data Browser has the proper structure to |
---|
98 | // successfully write out to an HDF5 file by :ref:`H5GW_WriteHDF5`. |
---|
99 | // |
---|
100 | // .. index:: ! HDF5___xref |
---|
101 | // |
---|
102 | // Structure of the *HDF5___xref* text wave |
---|
103 | // ===================================================== |
---|
104 | // |
---|
105 | // It is necessary to devise a method to correlate the name |
---|
106 | // of the same object in the HDF5 file with its representation in |
---|
107 | // the IgorPro data structure. In IgorPro, certain names are |
---|
108 | // reserved such that objects cannot be named. Routines exist |
---|
109 | // to substitute such names on data import to comply with |
---|
110 | // these restrictions. The routine *HDF5LoadGroup* performs |
---|
111 | // this substitution automatically, yet no routine is provided to |
---|
112 | // describe any name substitutions performed. |
---|
113 | // |
---|
114 | // The text wave, *HDF5___xref*, is created in the base folder of |
---|
115 | // the IgorPro folder structure to describe the mapping between |
---|
116 | // relative IgorPro and HDF5 path names, as shown in the next table. |
---|
117 | // This name was chosen in hopes that it might remain unique |
---|
118 | // and unused by others at the root level HDF5 files. |
---|
119 | // |
---|
120 | // HDF5___xref wave column plan |
---|
121 | // |
---|
122 | // ======= ================== |
---|
123 | // column description |
---|
124 | // ======= ================== |
---|
125 | // 0 HDF5 path |
---|
126 | // 1 Igor relative path |
---|
127 | // ======= ================== |
---|
128 | // |
---|
129 | // **Example** |
---|
130 | // |
---|
131 | // Consider the HDF5 file with datasets stored in this structure: |
---|
132 | // |
---|
133 | // .. code-block:: guess |
---|
134 | // :linenos: |
---|
135 | // |
---|
136 | // / |
---|
137 | // /sasentry01 |
---|
138 | // /sasdata01 |
---|
139 | // I |
---|
140 | // Q |
---|
141 | // |
---|
142 | // The next table shows the contents of *HDF5___xref* once this |
---|
143 | // HDF5 is read by *H5GW_WriteHDF5()*: |
---|
144 | // |
---|
145 | // === ======================= ========================== |
---|
146 | // row ``HDF5___xref[row][0]`` ``HDF5___xref[row][1]`` |
---|
147 | // === ======================= ========================== |
---|
148 | // 0 / : |
---|
149 | // 1 /sasentry01 :sasentry01 |
---|
150 | // 2 /sasentry01/sasdata01 :sasentry01:sasdata01 |
---|
151 | // 3 /sasentry01/sasdata01/I :sasentry01:sasdata01:I0 |
---|
152 | // 4 /sasentry01/sasdata01/Q :sasentry01:sasdata01:Q0 |
---|
153 | // === ======================= ========================== |
---|
154 | // |
---|
155 | // Remember, column 0 is for HDF5 paths, column 1 is for IgorPro paths. |
---|
156 | // |
---|
157 | // On reading an HDF5 file, the *file_name* and *file_path* are written to the |
---|
158 | // wave note of *HDF5___xref*. These notations are strictly informative and |
---|
159 | // are not used further by this interface. When writing back to HDF5, any |
---|
160 | // wave notes of the *HDF5___xref* wave are ignored. |
---|
161 | // |
---|
162 | // .. rubric:: About *HDF5___xref*: |
---|
163 | // |
---|
164 | // * Only the folders and waves listed in the *HDF5___xref* text |
---|
165 | // wave will be written to the HDF5 file. |
---|
166 | // * The *HDF5___xref* text wave is **not written** to the HDF5 file. |
---|
167 | // |
---|
168 | // When writing an HDF5 file with these functions, |
---|
169 | // based on the structure expected in an IgorPro data folder structure, |
---|
170 | // the *HDF5___xref* text wave is required. Each IgorPro object described |
---|
171 | // must exist as either an IgorPro folder or wave. A wave note is optional. |
---|
172 | // For each such IgorPro object, a corresponding HDF5 file object will be created. |
---|
173 | // |
---|
174 | // .. note:: Important! Any IgorPro data storage objects (folders or waves) |
---|
175 | // not listed in *HDF5___xref* **will not be written** to the HDF5 file. |
---|
176 | // |
---|
177 | // .. index:: group |
---|
178 | // .. index:: folder |
---|
179 | // |
---|
180 | // Groups and Folders |
---|
181 | // ===================== |
---|
182 | // |
---|
183 | // An HDF5 *group* corresponds to the IgorPro *folder*. Both are containers |
---|
184 | // for either data or containers. |
---|
185 | // |
---|
186 | // .. index:: Igor___folder_attributes |
---|
187 | // |
---|
188 | // In HDF5, a group may have attached metadata |
---|
189 | // known as *attributes*. In IgorPro, folders have no provision to store |
---|
190 | // attributes, thus an optional *Igor___folder_attributes* wave is created. The |
---|
191 | // folder attributes are stored in the wave note of this wave. For more information |
---|
192 | // about attributes, see the discussion of :ref:`attributes` below. |
---|
193 | // |
---|
194 | // .. index:: datasets |
---|
195 | // .. index:: waves |
---|
196 | // |
---|
197 | // Datasets and Waves |
---|
198 | // ====================== |
---|
199 | // |
---|
200 | // Data is stored in HDF5 datasets and IgorPro waves. |
---|
201 | // Both objects are capable of storing a variety of data types |
---|
202 | // with different shapes (rank and length). Of the two systems, |
---|
203 | // IgorPro is the more restrictive, limiting the rank of stored data |
---|
204 | // to four dimensions. |
---|
205 | // |
---|
206 | // Keep in mind that all components of a single dataset (or wave) are |
---|
207 | // of the same data type (such as 64-bit float or 8-bit int). |
---|
208 | // |
---|
209 | // In HDF5, a dataset may have attached metadata known as |
---|
210 | // *attributes*. HDF5 attributes are data structures in their own |
---|
211 | // right and may contain data structures. In IgorPro, waves have |
---|
212 | // a provision to store attributes in a text construct called the *wave note*. |
---|
213 | // Of these two, IgorPro is the more restrictive, unless one creates |
---|
214 | // a new wave to hold the data structure of the attributes. |
---|
215 | // For more information |
---|
216 | // about attributes, see the discussion of :ref:`attributes` below. |
---|
217 | // |
---|
218 | // The HDF5 library used by this package will take care of converting |
---|
219 | // between HDF5 datasets and IgorPro waves and the user need |
---|
220 | // not be too concerned about this. |
---|
221 | // |
---|
222 | // |
---|
223 | // .. index:: attributes |
---|
224 | // .. index:: ! Igor___folder_attributes |
---|
225 | // |
---|
226 | // .. _attributes: |
---|
227 | // |
---|
228 | // Attributes and Wave Notes |
---|
229 | // ============================================ |
---|
230 | // |
---|
231 | // Metadata about each of the objects in HDF5 files and IgorPro folders |
---|
232 | // is provided by *attributes*. In HDF5, these are attributes directly attached |
---|
233 | // to the object (group or dataset). In IgorPro, these attributes are **stored as text** in |
---|
234 | // different places depending on the type of the object, as shown in this table: |
---|
235 | // |
---|
236 | // ======== ======================================================= |
---|
237 | // object description |
---|
238 | // ======== ======================================================= |
---|
239 | // folder attributes are stored in the wave note of a special |
---|
240 | // wave in the folder named *Igor___folder_attributes* |
---|
241 | // wave attributes are stored in the wave note of the wave |
---|
242 | // ======== ======================================================= |
---|
243 | // |
---|
244 | // .. note:: IgorPro folders do not have a *wave note* |
---|
245 | // |
---|
246 | // HDF5 allows an attribute to be a data structure with the same rules for |
---|
247 | // complexity as a dataset except that attributes must be attached to a dataset |
---|
248 | // and cannot themselves have attributes. |
---|
249 | // |
---|
250 | // .. note:: In IgorPro, attributes will be stored as text. |
---|
251 | // |
---|
252 | // An IgorPro wave note is a text string that is used here to store a list of |
---|
253 | // *key,value* pairs. IgorPro provides helpful routines to manipulate such |
---|
254 | // lists, especially when used as wave notes. The IgorPro wave note is the most |
---|
255 | // natural representation of an *attribute* except that it does not preserve |
---|
256 | // the data structure of an HDF5 attribute without additional coding. This |
---|
257 | // limitation is deemed acceptable for this work. |
---|
258 | // |
---|
259 | // It is most obvious to see |
---|
260 | // the conversion of attributes into text by reading and HDF5 file and then |
---|
261 | // writing it back out to a new file. The data type of the HDF5 attributes will |
---|
262 | // likely be changed from its original type into "string, variable length". If this |
---|
263 | // is not acceptable, more work must be done in the routines below. |
---|
264 | // |
---|
265 | // IgorPro key,value list for the attributes |
---|
266 | // ---------------------------------------------------------------------------------------- |
---|
267 | // |
---|
268 | // Attributes are represented in IgorPro wave notes using a |
---|
269 | // list of *key,value* pairs. For example: |
---|
270 | // |
---|
271 | // .. code-block:: guess |
---|
272 | // :linenos: |
---|
273 | // |
---|
274 | // NX_class=SASdata |
---|
275 | // Q_indices=0,1 |
---|
276 | // I_axes=Q,Q |
---|
277 | // Mask_indices=0,1 |
---|
278 | // |
---|
279 | // It is important to know the delimiters used by this string to |
---|
280 | // differentiate various attributes, some of which may have a |
---|
281 | // list of values. Please refer to this table: |
---|
282 | // |
---|
283 | // =========== ==== ========================================== |
---|
284 | // separator char description |
---|
285 | // =========== ==== ========================================== |
---|
286 | // keySep = between *key* and *value* |
---|
287 | // itemSep , between multiple items in *value* |
---|
288 | // listSep \\r between multiple *key,value* pairs |
---|
289 | // =========== ==== ========================================== |
---|
290 | // |
---|
291 | // .. note:: A proposition is to store these values in a text wave |
---|
292 | // at the base of the folder structure and then use these value |
---|
293 | // throughout the folder. This can allow some flexibility with other |
---|
294 | // code and to make obvious which terms are used. |
---|
295 | // |
---|
296 | // .. index:: example |
---|
297 | // |
---|
298 | // Examples |
---|
299 | // ==================== |
---|
300 | // |
---|
301 | // Export data from IgorPro |
---|
302 | // ------------------------------------------------------- |
---|
303 | // |
---|
304 | // To write a simple dataset *I(Q)*, one might write this IgorPro code: |
---|
305 | // |
---|
306 | // .. code-block:: guess |
---|
307 | // :linenos: |
---|
308 | // |
---|
309 | // // create the folder structure |
---|
310 | // NewDataFolder/O/S root:mydata |
---|
311 | // NewDataFolder/O sasentry |
---|
312 | // NewDataFolder/O :sasentry:sasdata |
---|
313 | // |
---|
314 | // // create the waves |
---|
315 | // Make :sasentry:sasdata:I0 |
---|
316 | // Make :sasentry:sasdata:Q0 |
---|
317 | // |
---|
318 | // Make/N=0 Igor___folder_attributes |
---|
319 | // Make/N=0 :sasentry:Igor___folder_attributes |
---|
320 | // Make/N=0 :sasentry:sasdata:Igor___folder_attributes |
---|
321 | // |
---|
322 | // // create the attributes |
---|
323 | // Note/K Igor___folder_attributes, "producer=IgorPro\rNX_class=NXroot" |
---|
324 | // Note/K :sasentry:Igor___folder_attributes, "NX_class=NXentry" |
---|
325 | // Note/K :sasentry:sasdata:Igor___folder_attributes, "NX_class=NXdata" |
---|
326 | // Note/K :sasentry:sasdata:I0, "units=1/cm\rsignal=1\rtitle=reduced intensity" |
---|
327 | // Note/K :sasentry:sasdata:Q0, "units=1/A\rtitle=|scattering vector|" |
---|
328 | // |
---|
329 | // // create the cross-reference mapping |
---|
330 | // Make/T/N=(5,2) HDF5___xref |
---|
331 | // Edit/K=0 'HDF5___xref';DelayUpdate |
---|
332 | // HDF5___xref[0][1] = ":" |
---|
333 | // HDF5___xref[1][1] = ":sasentry" |
---|
334 | // HDF5___xref[2][1] = ":sasentry:sasdata" |
---|
335 | // HDF5___xref[3][1] = ":sasentry:sasdata:I0" |
---|
336 | // HDF5___xref[4][1] = ":sasentry:sasdata:Q0" |
---|
337 | // HDF5___xref[0][0] = "/" |
---|
338 | // HDF5___xref[1][0] = "/sasentry" |
---|
339 | // HDF5___xref[2][0] = "/sasentry/sasdata" |
---|
340 | // HDF5___xref[3][0] = "/sasentry/sasdata/I" |
---|
341 | // HDF5___xref[4][0] = "/sasentry/sasdata/Q" |
---|
342 | // |
---|
343 | // // Check our work so far. |
---|
344 | // // If something prints, there was an error above. |
---|
345 | // print H5GW_ValidateFolder("root:mydata") |
---|
346 | // |
---|
347 | // // set I0 and Q0 to your data |
---|
348 | // |
---|
349 | // print H5GW_WriteHDF5("root:mydata", "mydata.h5") |
---|
350 | // |
---|
351 | // .. index:: read |
---|
352 | // |
---|
353 | // Read data into IgorPro |
---|
354 | // ------------------------------------------------------- |
---|
355 | // |
---|
356 | // .. index:: example |
---|
357 | // |
---|
358 | // This is a simple operation, reading the file from the previous example into a new folder: |
---|
359 | // |
---|
360 | // .. code-block:: guess |
---|
361 | // :linenos: |
---|
362 | // |
---|
363 | // NewDataFolder/O/S root:newdata |
---|
364 | // H5GW_ReadHDF5("", "mydata.h5") // reads into current folder |
---|
365 | //@- |
---|
366 | |
---|
367 | // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // |
---|
368 | |
---|
369 | |
---|
370 | //@+ |
---|
371 | // .. index:: read |
---|
372 | // |
---|
373 | // Public Functions |
---|
374 | // ====================================== |
---|
375 | // |
---|
376 | // .. index:: ! H5GW_ReadHDF5() |
---|
377 | // |
---|
378 | // .. _H5GW_ReadHDF5: |
---|
379 | // |
---|
380 | // H5GW_ReadHDF5(parentFolder, fileName, [hdf5Path]) |
---|
381 | // ------------------------------------------------------------------------------------------------------------- |
---|
382 | // |
---|
383 | // Read the HDF5 data file *fileName* (located in directory *data*, |
---|
384 | // an IgorPro path variable) and store it in a subdirectory of |
---|
385 | // IgorPro folder *parentFolder*. |
---|
386 | // |
---|
387 | // At present, the *hdf5Path* parameter is not used. It is planned |
---|
388 | // (for the future) to use this to indicate reading only part of the |
---|
389 | // HDF5 file to be read. |
---|
390 | // |
---|
391 | // :String parentFolder: Igor folder path (default is current folder) |
---|
392 | // :String fileName: name of file (with extension), |
---|
393 | // either relative to current file system directory, |
---|
394 | // or include absolute file system path |
---|
395 | // :String hdf5Path: path of HDF file to load (default is "/") |
---|
396 | // :return String: Status: ""=no error, otherwise, error is described in text |
---|
397 | //@- |
---|
398 | |
---|
399 | Function/S H5GW_ReadHDF5(parentFolder, fileName, [hdf5Path]) |
---|
400 | String parentFolder, fileName, hdf5Path |
---|
401 | if ( ParamIsDefault(hdf5Path) ) |
---|
402 | hdf5Path = "/" |
---|
403 | endif |
---|
404 | |
---|
405 | String status = "" |
---|
406 | String oldFolder |
---|
407 | oldFolder = GetDataFolder(1) |
---|
408 | parentFolder = H5GW__SetStringDefault(parentFolder, oldFolder) |
---|
409 | |
---|
410 | // First, check that parentFolder exists |
---|
411 | if ( DataFolderExists(parentFolder) ) |
---|
412 | SetDataFolder $parentFolder |
---|
413 | else |
---|
414 | return parentFolder + " (Igor folder) not found" |
---|
415 | endif |
---|
416 | |
---|
417 | |
---|
418 | // do the work here: |
---|
419 | Variable/G fileID = H5GW__OpenHDF5_RO(fileName) |
---|
420 | if ( fileID == 0 ) |
---|
421 | return fileName + ": could not open as HDF5 file" |
---|
422 | endif |
---|
423 | |
---|
424 | //s_tic() //fast |
---|
425 | |
---|
426 | SVAR tmpStr=root:file_name |
---|
427 | fileName=tmpStr //SRK - in case the file was chosen from a dialog |
---|
428 | |
---|
429 | // read the data (too bad that HDF5LoadGroup does not read the attributes) |
---|
430 | String base_name = StringFromList(0,FileName,".") |
---|
431 | HDF5LoadGroup/Z/L=7/O/R/T=$base_name :, fileID, hdf5Path // recursive |
---|
432 | if ( V_Flag != 0 ) |
---|
433 | SetDataFolder $oldFolder |
---|
434 | return fileName + ": problem while opening HDF5 file" |
---|
435 | endif |
---|
436 | |
---|
437 | //s_toc() |
---|
438 | |
---|
439 | //s_tic() // this is the slow part, 0.7s for Igor-generated. > 9s for NICE (which has DAS_log) |
---|
440 | |
---|
441 | String/G objectPaths = S_objectPaths // this gives a clue to renamed datasets (see below for attributes) |
---|
442 | // read the attributes |
---|
443 | H5GW__HDF5ReadAttributes(fileID, hdf5Path, base_name) |
---|
444 | HDF5CloseFile fileID |
---|
445 | |
---|
446 | String/G file_path |
---|
447 | String/G group_name_list |
---|
448 | String/G dataset_name_list |
---|
449 | String file_info |
---|
450 | sprintf file_info, ":%s:HDF5___xref", base_name |
---|
451 | Note/K $file_info, "file_name="+fileName |
---|
452 | Note $file_info, "file_path="+file_path |
---|
453 | |
---|
454 | KillStrings/Z file_path, objectPaths, group_name_list, dataset_name_list // ,file_name |
---|
455 | KillVariables/Z fileID |
---|
456 | |
---|
457 | //s_toc() |
---|
458 | |
---|
459 | SetDataFolder $oldFolder |
---|
460 | return status |
---|
461 | End |
---|
462 | |
---|
463 | |
---|
464 | // ====================================== |
---|
465 | //@+ |
---|
466 | // .. index:: write |
---|
467 | // .. index:: ! H5GW_WriteHDF5() |
---|
468 | // |
---|
469 | // .. _H5GW_WriteHDF5: |
---|
470 | // |
---|
471 | // H5GW_WriteHDF5(parentFolder, newFileName) |
---|
472 | // ------------------------------------------------------------------------------------------------------------- |
---|
473 | // |
---|
474 | // Starting with an IgorPro folder constructed such that it passes the :ref:`H5GW_ValidateFolder` test, |
---|
475 | // write the components described in *HDF5___xref* to *newFileName*. |
---|
476 | // |
---|
477 | // :String parentFolder: Igor folder path (default is current folder) |
---|
478 | // :String fileName: name of file (with extension), |
---|
479 | // either relative to current file system directory, |
---|
480 | // or include absolute file system path |
---|
481 | //@- |
---|
482 | Function/T H5GW_WriteHDF5(parentFolder, newFileName, [replace]) |
---|
483 | String parentFolder, newFileName |
---|
484 | Variable replace |
---|
485 | if ( ParamIsDefault(replace) ) |
---|
486 | replace = 1 |
---|
487 | endif |
---|
488 | |
---|
489 | String status = "" |
---|
490 | String oldFolder = GetDataFolder(1) |
---|
491 | |
---|
492 | // First, check that parentFolder exists |
---|
493 | status = H5GW_ValidateFolder(parentFolder) |
---|
494 | if ( strlen(status) > 0 ) |
---|
495 | return status |
---|
496 | endif |
---|
497 | SetDataFolder $parentFolder |
---|
498 | |
---|
499 | // Build HDF5 group structure |
---|
500 | Variable fileID = H5GW__OpenHDF5_RW(newFileName, replace) |
---|
501 | if (fileID == 0) |
---|
502 | SetDataFolder $oldFolder |
---|
503 | return "Could not create HDF5 file " + newFileName + " for writing" |
---|
504 | endif |
---|
505 | |
---|
506 | // write datasets and attributes based on HDF5___xref table |
---|
507 | status = H5GW__WriteHDF5_Data(fileID) |
---|
508 | if ( strlen(status) > 0 ) |
---|
509 | HDF5CloseFile fileID |
---|
510 | SetDataFolder $oldFolder |
---|
511 | return status |
---|
512 | endif |
---|
513 | |
---|
514 | HDF5CloseFile fileID |
---|
515 | |
---|
516 | SetDataFolder $oldFolder |
---|
517 | return status // report success |
---|
518 | End |
---|
519 | |
---|
520 | |
---|
521 | |
---|
522 | // ====================================== |
---|
523 | //@+ |
---|
524 | // .. index:: validate |
---|
525 | // .. index:: ! H5GW_ValidateFolder() |
---|
526 | // |
---|
527 | // .. _H5GW_ValidateFolder: |
---|
528 | // |
---|
529 | // H5GW_ValidateFolder(parentFolder) |
---|
530 | // ------------------------------------------------------------------------------------------------------------- |
---|
531 | // |
---|
532 | // Check (validate) that a given IgorPro folder has the necessary |
---|
533 | // structure for the function H5GW__WriteHDF5_Data(fileID) to be |
---|
534 | // successful when writing that folder to an HDF5 file. |
---|
535 | // |
---|
536 | // :String parentFolder: Igor folder path (default is current folder) |
---|
537 | // :return String: Status: ""=no error, otherwise, error is described in text |
---|
538 | //@- |
---|
539 | |
---|
540 | Function/T H5GW_ValidateFolder(parentFolder) |
---|
541 | String parentFolder |
---|
542 | String oldFolder = GetDataFolder(1) |
---|
543 | // First, check that parentFolder exists |
---|
544 | if ( DataFolderExists(parentFolder) ) |
---|
545 | SetDataFolder $parentFolder |
---|
546 | else |
---|
547 | return parentFolder + " (Igor folder) not found" |
---|
548 | endif |
---|
549 | |
---|
550 | if (1 != Exists("HDF5___xref")) |
---|
551 | SetDataFolder $oldFolder |
---|
552 | return "required wave (HDF5___xref) is missing in folder: " + parentFolder |
---|
553 | endif |
---|
554 | Wave/T HDF5___xref |
---|
555 | if ( DimSize(HDF5___xref, 1) != 2 ) |
---|
556 | SetDataFolder $oldFolder |
---|
557 | return "text wave HDF5___xref must be of shape (N,2)" |
---|
558 | endif |
---|
559 | |
---|
560 | Variable length = DimSize(HDF5___xref, 0), ii |
---|
561 | String item, msg |
---|
562 | for (ii=0; ii < length; ii=ii+1) |
---|
563 | item = HDF5___xref[ii][1] |
---|
564 | if ( (1 != DataFolderExists(item)) && (1 != Exists(item)) ) |
---|
565 | SetDataFolder $oldFolder |
---|
566 | return "specified IgorPro object " + item + " was not found in folder " + parentFolder |
---|
567 | endif |
---|
568 | // TODO: Check that each corresponding HDF5___xref[ii][0] is a valid HDF5 path name |
---|
569 | if ( itemsInList(item, ":") != itemsInList(HDF5___xref[ii][0], "/") ) |
---|
570 | SetDataFolder $oldFolder |
---|
571 | msg = "different lengths between HDF5 and IgorPro paths on row" + num2str(ii) + "of HDF5___xref" |
---|
572 | return msg |
---|
573 | endif |
---|
574 | endfor |
---|
575 | |
---|
576 | // TODO: more validation steps |
---|
577 | |
---|
578 | SetDataFolder $oldFolder |
---|
579 | return "" |
---|
580 | End |
---|
581 | |
---|
582 | |
---|
583 | // ====================================== |
---|
584 | //@+ |
---|
585 | // .. index:: test |
---|
586 | // .. index:: ! H5GW_TestSuite() |
---|
587 | // |
---|
588 | // .. _H5GW_TestSuite: |
---|
589 | // |
---|
590 | // H5GW_TestSuite() |
---|
591 | // ------------------------------------------------------------------------------------------------------------- |
---|
592 | // |
---|
593 | // Test the routines in this file using the supplied test data files. |
---|
594 | // HDF5 data files are obtained from the canSAS 2012 repository of |
---|
595 | // HDF5 examples |
---|
596 | // (http://www.cansas.org/formats/canSAS2012/1.0/doc/_downloads/simpleexamplefile.h5). |
---|
597 | //@- |
---|
598 | |
---|
599 | Function H5GW_TestSuite() |
---|
600 | String listSep = ";" |
---|
601 | String fileExt = ".h5" |
---|
602 | String parentDir = "root:worker" |
---|
603 | |
---|
604 | String name_list = "simpleexamplefile" |
---|
605 | name_list = name_list +listSep + "simple2dcase" |
---|
606 | name_list = name_list +listSep + "simple2dmaskedcase" |
---|
607 | name_list = name_list +listSep + "generic2dqtimeseries" |
---|
608 | name_list = name_list +listSep + "generic2dtimetpseries" |
---|
609 | |
---|
610 | Variable length = itemsInList(name_list, listSep), ii |
---|
611 | String name, newName, newerName |
---|
612 | for (ii = 0; ii < length; ii = ii + 1) |
---|
613 | name = StringFromList(ii, name_list, listSep) + fileExt |
---|
614 | // Test reading the HDF5 file and then writing the data to a new HDF5 file |
---|
615 | newName = H5GW__TestFile(parentDir, name) |
---|
616 | // Apply the test again on the new HDF5 file |
---|
617 | newerName = H5GW__TestFile(parentDir, newName) |
---|
618 | endfor |
---|
619 | End |
---|
620 | |
---|
621 | |
---|
622 | // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // |
---|
623 | |
---|
624 | |
---|
625 | // ====================================== |
---|
626 | //@+ |
---|
627 | // .. index:: functions; private (static) |
---|
628 | // |
---|
629 | // Private (static) Functions |
---|
630 | // ====================================== |
---|
631 | // |
---|
632 | // Documentation of some, but not all, private functions is provided. |
---|
633 | // |
---|
634 | //@- |
---|
635 | |
---|
636 | |
---|
637 | //@+ |
---|
638 | // .. index:: ! H5GW__OpenHDF5_RW() |
---|
639 | // |
---|
640 | // H5GW__OpenHDF5_RW(newFileName, replace) |
---|
641 | // ------------------------------------------------------------------------------------------------------------- |
---|
642 | //@- |
---|
643 | static Function H5GW__OpenHDF5_RW(newFileName, replace) |
---|
644 | String newFileName |
---|
645 | Variable replace |
---|
646 | Variable fileID |
---|
647 | if (replace) |
---|
648 | HDF5CreateFile/P=home/Z/O fileID as newFileName |
---|
649 | else |
---|
650 | // make sure file does not exist now, or handle better |
---|
651 | HDF5CreateFile/P=home/Z fileID as newFileName |
---|
652 | endif |
---|
653 | if (V_Flag != 0) |
---|
654 | return 0 |
---|
655 | endif |
---|
656 | return fileID |
---|
657 | End |
---|
658 | |
---|
659 | |
---|
660 | //@+ |
---|
661 | // .. index:: ! H5GW__WriteHDF5_Data() |
---|
662 | // |
---|
663 | // H5GW__WriteHDF5_Data(fileID) |
---|
664 | // ------------------------------------------------------------------------------------------------------------- |
---|
665 | //@- |
---|
666 | static Function/T H5GW__WriteHDF5_Data(fileID) |
---|
667 | Variable fileID |
---|
668 | String status = "" |
---|
669 | |
---|
670 | Wave/t xref = HDF5___xref |
---|
671 | Variable HDF5_col = 0 |
---|
672 | Variable Igor_col = 1 |
---|
673 | Variable rows = DimSize(xref,0), ii, groupID, length, jj |
---|
674 | String igorPath, hdf5Path, dataType, folder_attr_info, notes, item, key, value |
---|
675 | for (ii = 0; ii < rows; ii=ii+1) |
---|
676 | igorPath = xref[ii][Igor_col] |
---|
677 | hdf5Path = xref[ii][HDF5_col] |
---|
678 | // print DataFolderExists(igorPath), igorPath, " --> ", hdf5Path |
---|
679 | if ( DataFolderExists(igorPath) ) |
---|
680 | // group |
---|
681 | if ( cmpstr("/", hdf5Path) != 0 ) |
---|
682 | HDF5CreateGroup /Z fileID , hdf5Path, groupID |
---|
683 | if (V_Flag != 0) |
---|
684 | status = H5GW__AppendString(status, "\r", "problem creating HDF5 group: " + hdf5Path) |
---|
685 | endif |
---|
686 | else |
---|
687 | groupID = fileID |
---|
688 | endif |
---|
689 | // attributes |
---|
690 | notes = "" |
---|
691 | folder_attr_info = H5GW__appendPathDelimiter(igorPath, ":") + "Igor___folder_attributes" |
---|
692 | Wave/Z folder_attr = $folder_attr_info |
---|
693 | // SRK - folder_attr wave may not exist if HDF file not written out by HDF5Gateway. Handle the error |
---|
694 | if(WaveExists(folder_attr)) |
---|
695 | notes = note(folder_attr) |
---|
696 | length = itemsInList(notes, "\r") |
---|
697 | String response |
---|
698 | if ( length > 0 ) |
---|
699 | for (jj = 0; jj < length; jj=jj+1 ) |
---|
700 | item = StringFromList(jj, notes,"\r") |
---|
701 | key = StringFromList(0, item,"=") |
---|
702 | value = StringFromList(1, item,"=") |
---|
703 | response = H5GW__SetTextAttributeHDF5(fileID, key, value, hdf5Path) |
---|
704 | status = H5GW__AppendString(status, "\r", response) |
---|
705 | endfor |
---|
706 | endif |
---|
707 | endif // SRK check if folder_attr exists |
---|
708 | else |
---|
709 | // dataset |
---|
710 | Wave theWave = $igorPath |
---|
711 | HDF5SaveData/IGOR=0/Z theWave, fileID, hdf5Path |
---|
712 | if (V_Flag != 0) |
---|
713 | status = H5GW__AppendString(status, "\r", "problem saving HDF5 dataset: " + hdf5Path) |
---|
714 | endif |
---|
715 | |
---|
716 | // look at the wave note for any attributes |
---|
717 | notes = note(theWave) |
---|
718 | length = itemsInList(notes, "\r") |
---|
719 | if ( length > 0 ) |
---|
720 | for (jj = 0; jj < length; jj=jj+1 ) |
---|
721 | item = StringFromList(jj, notes,"\r") |
---|
722 | key = StringFromList(0, item,"=") |
---|
723 | value = StringFromList(1, item,"=") |
---|
724 | H5GW__SetTextAttributeHDF5(fileID, key, value, hdf5Path) |
---|
725 | endfor |
---|
726 | endif |
---|
727 | |
---|
728 | endif |
---|
729 | endfor |
---|
730 | |
---|
731 | return status |
---|
732 | End |
---|
733 | |
---|
734 | |
---|
735 | //@+ |
---|
736 | // .. index:: ! H5GW__SetHDF5ObjectAttributes() |
---|
737 | // |
---|
738 | // H5GW__SetHDF5ObjectAttributes(itemID, igorPath, hdf5Path) |
---|
739 | // ------------------------------------------------------------------------------------------------------------- |
---|
740 | //@- |
---|
741 | static Function H5GW__SetHDF5ObjectAttributes(itemID, igorPath, hdf5Path) |
---|
742 | Variable itemID |
---|
743 | String igorPath, hdf5Path |
---|
744 | Wave theWave = $igorPath |
---|
745 | String notes = note(theWave), item, key, value |
---|
746 | Variable jj, length = itemsInList(notes, "\r") |
---|
747 | notes = note(theWave) |
---|
748 | length = itemsInList(notes, "\r") |
---|
749 | if ( length > 0 ) |
---|
750 | for (jj = 0; jj < length; jj=jj+1 ) |
---|
751 | item = StringFromList(jj, notes,"\r") |
---|
752 | key = StringFromList(0, item,"=") |
---|
753 | value = StringFromList(1, item,"=") |
---|
754 | H5GW__SetTextAttributeHDF5(itemID, key, value, hdf5Path) |
---|
755 | endfor |
---|
756 | endif |
---|
757 | End |
---|
758 | |
---|
759 | |
---|
760 | //@+ |
---|
761 | // .. index:: ! H5GW__SetTextAttributeHDF5() |
---|
762 | // |
---|
763 | // H5GW__SetTextAttributeHDF5(itemID, name, value, hdf5Path) |
---|
764 | // ------------------------------------------------------------------------------------------------------------- |
---|
765 | //@- |
---|
766 | static Function/T H5GW__SetTextAttributeHDF5(itemID, name, value, hdf5Path) |
---|
767 | Variable itemID |
---|
768 | String name, value, hdf5Path |
---|
769 | String status = "" |
---|
770 | Make/T/N=(1) H5GW____temp |
---|
771 | H5GW____temp[0] = value |
---|
772 | HDF5SaveData/Z/A=name H5GW____temp, itemID, hdf5Path |
---|
773 | if ( V_Flag != 0) |
---|
774 | status = "problem saving HDF5 text attribute: " + hdf5Path |
---|
775 | endif |
---|
776 | KillWaves H5GW____temp |
---|
777 | return status |
---|
778 | End |
---|
779 | |
---|
780 | |
---|
781 | // ====================================== |
---|
782 | //@+ |
---|
783 | // .. index:: ! H5GW__make_xref() |
---|
784 | // |
---|
785 | // H5GW__make_xref(parentFolder, objectPaths, group_name_list, dataset_name_list, base_name) |
---|
786 | // --------------------------------------------------------------------------------------------------------------------------------------------------------- |
---|
787 | // |
---|
788 | // Analyze the mapping between HDF5 objects and Igor paths |
---|
789 | // Store the discoveries of this analysis in the HDF5___xref text wave |
---|
790 | // |
---|
791 | // :String parentFolder: Igor folder path (default is current folder) |
---|
792 | // :String objectPaths: Igor paths to data objects |
---|
793 | // :String group_name_list: |
---|
794 | // :String dataset_name_list: |
---|
795 | // :String base_name: |
---|
796 | // |
---|
797 | // HDF5___xref wave column plan |
---|
798 | // |
---|
799 | // ====== =============================== |
---|
800 | // column description |
---|
801 | // ====== =============================== |
---|
802 | // 0 HDF5 path |
---|
803 | // 1 Igor relative path |
---|
804 | // ====== =============================== |
---|
805 | //@- |
---|
806 | |
---|
807 | static Function H5GW__make_xref(parentFolder, objectPaths, group_name_list, ds_list, base_name) |
---|
808 | String parentFolder, objectPaths, group_name_list, ds_list, base_name |
---|
809 | |
---|
810 | String xref = "" // key.value pair list as a string |
---|
811 | String keySep = "=" // between key and value |
---|
812 | String listSep = "\r" // between key,value pairs |
---|
813 | |
---|
814 | String matchStr = parentFolder + base_name |
---|
815 | String igorPaths = ReplaceString(matchStr, objectPaths, "") |
---|
816 | |
---|
817 | Variable ii, length |
---|
818 | String dataset, igorPath |
---|
819 | // compare items in ds_list and igorPaths |
---|
820 | length = itemsInList(ds_list, ";") |
---|
821 | if ( length == itemsInList(igorPaths, ";") ) |
---|
822 | for (ii = 0; ii < length; ii = ii + 1) |
---|
823 | // ASSUME this is the cross-reference list we need |
---|
824 | dataset = StringFromList(ii, ds_list, ";") |
---|
825 | igorPath = StringFromList(ii, igorPaths, ";") |
---|
826 | xref = H5GW__addPathXref(parentFolder, base_name, dataset, igorPath, xref, keySep, listSep) |
---|
827 | endfor |
---|
828 | else |
---|
829 | // TODO: report an error here and return |
---|
830 | endif |
---|
831 | |
---|
832 | // finally, write the xref contents to a wave |
---|
833 | length = itemsInList(xref, listSep) |
---|
834 | String file_info |
---|
835 | sprintf file_info, ":%s:HDF5___xref", base_name |
---|
836 | Make/O/N=(length,2)/T $file_info |
---|
837 | Wave/T file_infoT = $file_info |
---|
838 | String item |
---|
839 | Variable HDF5_col = 0 |
---|
840 | Variable Igor_col = 1 |
---|
841 | for (ii = 0; ii < length; ii=ii+1) |
---|
842 | item = StringFromList(ii, xref, listSep) |
---|
843 | file_infoT[ii][HDF5_col] = StringFromList(0, item, keySep) |
---|
844 | file_infoT[ii][Igor_col] = StringFromList(1, item, keySep) |
---|
845 | endfor |
---|
846 | |
---|
847 | End |
---|
848 | |
---|
849 | |
---|
850 | //@+ |
---|
851 | // .. index:: ! H5GW__addPathXref() |
---|
852 | // |
---|
853 | // H5GW__addPathXref(parentFolder, base_name, hdf5Path, igorPath, xref, keySep, listSep) |
---|
854 | // ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
---|
855 | //@- |
---|
856 | static Function/T H5GW__addPathXref(parentFolder, base_name, hdf5Path, igorPath, xref, keySep, listSep) |
---|
857 | String parentFolder, base_name, hdf5Path, igorPath, xref, keySep, listSep |
---|
858 | String result = xref |
---|
859 | // look through xref to find each component of full hdf5Path, including the dataset |
---|
860 | Variable ii, length = itemsInList(hdf5Path, "/") |
---|
861 | String hdf5 = "", path = "" |
---|
862 | for (ii = 0; ii < length; ii = ii + 1) |
---|
863 | hdf5 = H5GW__appendPathDelimiter(hdf5, "/") + StringFromList(ii, hdf5Path, "/") |
---|
864 | path = H5GW__appendPathDelimiter(path, ":") + StringFromList(ii, igorPath, ":") |
---|
865 | if ( strlen(StringByKey(hdf5, result, keySep, listSep)) == 0 ) |
---|
866 | result = H5GW__addXref(hdf5, path, result, keySep, listSep) |
---|
867 | endif |
---|
868 | endfor |
---|
869 | return result |
---|
870 | End |
---|
871 | |
---|
872 | |
---|
873 | //@+ |
---|
874 | // .. index:: ! H5GW__addXref() |
---|
875 | // |
---|
876 | // H5GW__addXref(key, value, xref, keySep, listSep) |
---|
877 | // ------------------------------------------------------------------------------------------------------------- |
---|
878 | // |
---|
879 | // append a new key,value pair to the cross-reference list |
---|
880 | //@- |
---|
881 | static Function/T H5GW__addXref(key, value, xref, keySep, listSep) |
---|
882 | String key, value, xref, keySep, listSep |
---|
883 | return H5GW__AppendString(xref, listSep, key + keySep + value) |
---|
884 | End |
---|
885 | |
---|
886 | |
---|
887 | //@+ |
---|
888 | // .. index:: ! H5GW__appendPathDelimiter() |
---|
889 | // |
---|
890 | // H5GW__appendPathDelimiter(str, sep) |
---|
891 | // ------------------------------------------------------------------------------------------------------------- |
---|
892 | //@- |
---|
893 | static Function/T H5GW__appendPathDelimiter(str, sep) |
---|
894 | String str, sep |
---|
895 | if ( (strlen(str) == 0) || ( cmpstr(sep, str[strlen(str)-1]) != 0) ) |
---|
896 | return str + sep |
---|
897 | endif |
---|
898 | return str |
---|
899 | End |
---|
900 | |
---|
901 | |
---|
902 | |
---|
903 | // ====================================== |
---|
904 | //@+ |
---|
905 | // .. index:: ! H5GW__findTextWaveIndex() |
---|
906 | // |
---|
907 | // H5GW__findTextWaveIndex(twave, str, col) |
---|
908 | // ------------------------------------------------------------------------------------------------------------- |
---|
909 | // |
---|
910 | // :Wave/T twave: correlation between HDF5 and Igor paths |
---|
911 | // :String str: text to be located in column *col* |
---|
912 | // :int col: column number to search for *str* |
---|
913 | // :returns int: index of found text or -1 if not found |
---|
914 | //@- |
---|
915 | |
---|
916 | static Function H5GW__findTextWaveIndex(twave, str, col) |
---|
917 | Wave/T twave |
---|
918 | String str |
---|
919 | Variable col |
---|
920 | Variable result = -1, ii, rows=DimSize(twave,0) |
---|
921 | for (ii=0; ii < rows; ii=ii+1) |
---|
922 | if (0 == cmpstr(str, twave[ii][col]) ) |
---|
923 | result = ii |
---|
924 | break |
---|
925 | endif |
---|
926 | endfor |
---|
927 | return result |
---|
928 | End |
---|
929 | |
---|
930 | |
---|
931 | // ====================================== |
---|
932 | //@+ |
---|
933 | // .. index:: ! H5GW__OpenHDF5_RO() |
---|
934 | // |
---|
935 | // H5GW__OpenHDF5_RO(fileName) |
---|
936 | // ------------------------------------------------------------------------------------------------------------- |
---|
937 | // |
---|
938 | // :String fileName: name of file (with extension), |
---|
939 | // either relative to current file system directory |
---|
940 | // or includes absolute file system path |
---|
941 | // :returns int: Status: 0 if error, non-zero (fileID) if successful |
---|
942 | // |
---|
943 | // Assumed Parameter: |
---|
944 | // |
---|
945 | // * *home* (path): Igor path name (defines a file system |
---|
946 | // directory in which to find the data files) |
---|
947 | // Note: data is not changed by this function |
---|
948 | //@- |
---|
949 | |
---|
950 | Static Function H5GW__OpenHDF5_RO(fileName) |
---|
951 | String fileName |
---|
952 | |
---|
953 | // SRK - now will present a dialog if the file can't be found |
---|
954 | // if ( H5GW__FileExists(fileName) == 0 ) |
---|
955 | // avoid the open file dialog if the file is not found here |
---|
956 | // return 0 |
---|
957 | // endif |
---|
958 | |
---|
959 | Variable fileID = 0 |
---|
960 | HDF5OpenFile/R/P=home/Z fileID as fileName |
---|
961 | // HDF5OpenFile/R/P=catPathName/Z fileID as fileName |
---|
962 | if (V_Flag != 0) |
---|
963 | return 0 |
---|
964 | endif |
---|
965 | |
---|
966 | if ( 0 ) |
---|
967 | STRUCT HDF5DataInfo di // Defined in HDF5 Browser.ipf. |
---|
968 | InitHDF5DataInfo(di) // Initialize structure. |
---|
969 | HDF5AttributeInfo(fileID, "/", 1, "file_name", 0, di) |
---|
970 | Print di |
---|
971 | endif |
---|
972 | |
---|
973 | |
---|
974 | String/G root:file_path = S_path |
---|
975 | String/G root:file_name = S_FileName |
---|
976 | return fileID |
---|
977 | End |
---|
978 | |
---|
979 | |
---|
980 | // ====================================== |
---|
981 | //@+ |
---|
982 | // .. index:: ! H5GW__HDF5ReadAttributes() |
---|
983 | // |
---|
984 | // H5GW__HDF5ReadAttributes(fileID, hdf5Path, baseName) |
---|
985 | // ------------------------------------------------------------------------------------------------------------- |
---|
986 | // |
---|
987 | // Reads and assigns the group and dataset attributes. |
---|
988 | // For groups, it creates a dummy wave *Igor___folder_attributes* |
---|
989 | // to hold the group attributes. |
---|
990 | // |
---|
991 | // All attributes are stored in the wave note |
---|
992 | // |
---|
993 | // Too bad that HDF5LoadGroup does not read the attributes. |
---|
994 | // |
---|
995 | // :int fileID: IgorPro reference number for this HDF5 file |
---|
996 | // :String hdf5Path: read the HDF5 file starting |
---|
997 | // from this level (default is the root level, "/") |
---|
998 | // Note: not implemented yet. |
---|
999 | // :String baseName: IgorPro subfolder name to |
---|
1000 | // store attributes. Maps directly from HDF5 path. |
---|
1001 | //@- |
---|
1002 | |
---|
1003 | Static Function H5GW__HDF5ReadAttributes(fileID, hdf5Path, baseName) |
---|
1004 | Variable fileID |
---|
1005 | String hdf5Path |
---|
1006 | String baseName |
---|
1007 | |
---|
1008 | Variable group_attributes_type = 1 |
---|
1009 | Variable dataset_attributes_type = 2 |
---|
1010 | |
---|
1011 | //s_tic() // 0.11 s |
---|
1012 | // read and assign group attributes |
---|
1013 | String S_HDF5ListGroup |
---|
1014 | HDF5ListGroup/F/R/TYPE=(group_attributes_type) fileID, hdf5Path // TYPE=1 reads groups |
---|
1015 | String/G group_name_list = hdf5Path + ";" + S_HDF5ListGroup |
---|
1016 | |
---|
1017 | Variable length = ItemsInList(group_name_list) |
---|
1018 | Variable index, i_attr |
---|
1019 | String group_name |
---|
1020 | String attr_name_list, attr_name, attribute_str |
---|
1021 | |
---|
1022 | String old_dir = GetDataFolder(1), subdir, group_attr_name |
---|
1023 | for (index = 0; index < length; index = index+1) |
---|
1024 | group_name = StringFromList(index, group_name_list) |
---|
1025 | attribute_str = H5GW__HDF5AttributesToString(fileID, group_name, group_attributes_type) |
---|
1026 | if ( strlen(attribute_str) > 0 ) |
---|
1027 | // store these attributes in the wavenote of a unique wave in the group |
---|
1028 | subdir = ":" + baseName + ReplaceString("/", group_name, ":") |
---|
1029 | SetDataFolder $subdir |
---|
1030 | group_attr_name = StringFromList((itemsInList(group_name, "/")-1), group_name, "/") |
---|
1031 | group_attr_name = H5GW__SetStringDefault(group_attr_name, "root") + "_attributes" |
---|
1032 | group_attr_name = "Igor___folder_attributes" |
---|
1033 | Make/O/N=0 $group_attr_name |
---|
1034 | Note/K $group_attr_name, attribute_str |
---|
1035 | endif |
---|
1036 | SetDataFolder $old_dir |
---|
1037 | endfor |
---|
1038 | |
---|
1039 | //s_toc() |
---|
1040 | //s_tic() // 3.2 s !! |
---|
1041 | |
---|
1042 | // read and assign dataset attributes |
---|
1043 | HDF5ListGroup/F/R/TYPE=(dataset_attributes_type) fileID, hdf5Path // TYPE=2 reads groups |
---|
1044 | String/G dataset_name_list = S_HDF5ListGroup |
---|
1045 | |
---|
1046 | // build a table connecting objectPaths with group_name_list and dataset_name_list |
---|
1047 | // using parentFolder and baseName |
---|
1048 | String parentFolder = GetDataFolder(1) |
---|
1049 | String/G objectPaths |
---|
1050 | H5GW__make_xref(parentFolder, objectPaths, group_name_list, dataset_name_list, baseName) |
---|
1051 | |
---|
1052 | String file_info |
---|
1053 | sprintf file_info, ":%s:HDF5___xref", baseName |
---|
1054 | Wave/T xref = $file_info |
---|
1055 | |
---|
1056 | //s_toc() |
---|
1057 | //s_tic() // worst @ 6.3 s |
---|
1058 | |
---|
1059 | Variable row |
---|
1060 | String hdf5_path, igor_path |
---|
1061 | length = ItemsInList(dataset_name_list) |
---|
1062 | for (index = 0; index < length; index = index+1) |
---|
1063 | hdf5_path = StringFromList(index, dataset_name_list) |
---|
1064 | attribute_str = H5GW__HDF5AttributesToString(fileID, hdf5_path, dataset_attributes_type) |
---|
1065 | if ( strlen(attribute_str) > 0 ) |
---|
1066 | // store these attributes in the wavenote of the dataset |
---|
1067 | row = H5GW__findTextWaveIndex(xref, hdf5_path, 0) |
---|
1068 | if (row > -1) |
---|
1069 | igor_path = ":" + baseName + xref[row][1] |
---|
1070 | wave targetWave=$igor_path |
---|
1071 | Note/K targetWave, attribute_str |
---|
1072 | endif |
---|
1073 | endif |
---|
1074 | endfor |
---|
1075 | //s_toc() |
---|
1076 | |
---|
1077 | End |
---|
1078 | |
---|
1079 | // ====================================== |
---|
1080 | //@+ |
---|
1081 | // .. index:: ! H5GW__HDF5AttributesToString() |
---|
1082 | // |
---|
1083 | // H5GW__HDF5AttributesToString(fileID, hdf5_Object, hdf5_Type, [keyDelimiter, keyValueSep, itemDelimiter]) |
---|
1084 | // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
---|
1085 | // |
---|
1086 | // Reads the attributes assigned to this object and returns |
---|
1087 | // a string of key=value pairs, delimited by ; |
---|
1088 | // Multiple values for a key are delimited by , |
---|
1089 | // |
---|
1090 | // All attributes are stored in the wave note |
---|
1091 | // |
---|
1092 | // Too bad that HDF5LoadGroup does not read the attributes. |
---|
1093 | // |
---|
1094 | // :int fileID: IgorPro reference number for this HDF5 file |
---|
1095 | // :String hdf5_Object: full HDF5 path of object |
---|
1096 | // :int hdf5_Type: 1=group, 2=dataset |
---|
1097 | // :String keyDelimiter: between key=value pairs, default = "\r" |
---|
1098 | // :String keyValueSep: key and value, default = "=" |
---|
1099 | // :String itemDelimiter: between multiple values, default = "," |
---|
1100 | // :returns str: key1=value;key2=value,value;key3=value, empty string if no attributes |
---|
1101 | //@- |
---|
1102 | |
---|
1103 | Static Function/T H5GW__HDF5AttributesToString(fileID, hdf5_Object, hdf5_Type, [keyDelimiter, keyValueSep, itemDelimiter]) |
---|
1104 | Variable fileID |
---|
1105 | String hdf5_Object |
---|
1106 | Variable hdf5_Type |
---|
1107 | String keyDelimiter |
---|
1108 | String keyValueSep |
---|
1109 | String itemDelimiter |
---|
1110 | |
---|
1111 | if ( ParamIsDefault(keyDelimiter) ) |
---|
1112 | keyDelimiter = "\r" |
---|
1113 | endif |
---|
1114 | if ( ParamIsDefault(keyValueSep) ) |
---|
1115 | keyValueSep = "=" |
---|
1116 | endif |
---|
1117 | if ( ParamIsDefault(itemDelimiter) ) |
---|
1118 | itemDelimiter = "," |
---|
1119 | endif |
---|
1120 | |
---|
1121 | String result = "" |
---|
1122 | String attr_name_list, attr_name, attr_str, temp_str |
---|
1123 | Variable num_attr, i_attr |
---|
1124 | HDF5ListAttributes/TYPE=(hdf5_Type)/Z fileID, hdf5_Object |
---|
1125 | if ( V_Flag != 0 ) |
---|
1126 | return result |
---|
1127 | endif |
---|
1128 | attr_name_list = S_HDF5ListAttributes |
---|
1129 | num_attr = ItemsInList(attr_name_list) |
---|
1130 | for (i_attr = 0; i_attr < num_attr; i_attr = i_attr+1) |
---|
1131 | attr_name = StringFromList(i_attr, attr_name_list) |
---|
1132 | attr_str = H5GW__HDF5AttributeDataToString(fileID, hdf5_Object, hdf5_Type, attr_name, itemDelimiter) |
---|
1133 | if (strlen(result) > 0) |
---|
1134 | result = result + keyDelimiter |
---|
1135 | endif |
---|
1136 | result = result + attr_name + keyValueSep + attr_str |
---|
1137 | endfor |
---|
1138 | KillWaves/Z attr_wave |
---|
1139 | |
---|
1140 | return result |
---|
1141 | End |
---|
1142 | |
---|
1143 | |
---|
1144 | // ====================================== |
---|
1145 | //@+ |
---|
1146 | // .. index:: ! H5GW__HDF5AttributeDataToString() |
---|
1147 | // |
---|
1148 | // H5GW__HDF5AttributeDataToString(fileID, hdf5_Object, hdf5_Type, attr_name, itemDelimiter) |
---|
1149 | // --------------------------------------------------------------------------------------------------------------------------------------------------- |
---|
1150 | // |
---|
1151 | // Reads the value of a specific attribute assigned to this object |
---|
1152 | // and returns its value. |
---|
1153 | // |
---|
1154 | // :int fileID: IgorPro reference number for this HDF5 file |
---|
1155 | // :String hdf5_Object: full HDF5 path of object |
---|
1156 | // :int hdf5_Type: 1=group, 2=dataset |
---|
1157 | // :String attr_name: name of the attribute |
---|
1158 | // :String itemDelimiter: if the attribute data is an array, |
---|
1159 | // this will delimit the representation of its members in a string |
---|
1160 | // :returns String: value, empty string if no value |
---|
1161 | //@- |
---|
1162 | |
---|
1163 | Static Function/T H5GW__HDF5AttributeDataToString(fileID, hdf5_Object, hdf5_Type, attr_name, itemDelimiter) |
---|
1164 | Variable fileID |
---|
1165 | String hdf5_Object |
---|
1166 | Variable hdf5_Type |
---|
1167 | String attr_name |
---|
1168 | String itemDelimiter |
---|
1169 | |
---|
1170 | String attr_str = "", temp_str |
---|
1171 | Variable index |
---|
1172 | HDF5LoadData/A=attr_name/TYPE=(hdf5_Type)/N=attr_wave/Z/O/Q fileID, hdf5_Object |
---|
1173 | if ( V_Flag == 0 ) |
---|
1174 | if ( 0 == cmpstr( "attr_wave,", WaveList("attr_wave", ",", "TEXT:1")) ) |
---|
1175 | Wave/T attr_waveT=attr_wave |
---|
1176 | attr_str = attr_waveT[0] |
---|
1177 | else |
---|
1178 | Wave attr_waveN=attr_wave |
---|
1179 | attr_str = "" |
---|
1180 | sprintf attr_str, "%g", attr_waveN[0] // assume at least one point |
---|
1181 | for ( index=1; index < numpnts(attr_waveN); index=index+1) |
---|
1182 | sprintf temp_str, "%g", attr_waveN[index] |
---|
1183 | attr_str = attr_str + itemDelimiter + temp_str |
---|
1184 | endfor |
---|
1185 | endif |
---|
1186 | endif |
---|
1187 | KillWaves/Z attr_wave |
---|
1188 | return attr_str |
---|
1189 | End |
---|
1190 | |
---|
1191 | |
---|
1192 | // ====================================== |
---|
1193 | //@+ |
---|
1194 | // .. index:: ! H5GW__SetStringDefault() |
---|
1195 | // |
---|
1196 | // H5GW__SetStringDefault(str, string_default) |
---|
1197 | // ------------------------------------------------------------------------------------------------------------- |
---|
1198 | // |
---|
1199 | // :String str: supplied value |
---|
1200 | // :String string_default: default value |
---|
1201 | // :returns String: default if supplied value is empty |
---|
1202 | //@- |
---|
1203 | |
---|
1204 | Static Function/T H5GW__SetStringDefault(str, string_default) |
---|
1205 | String str |
---|
1206 | String string_default |
---|
1207 | String result = string_default |
---|
1208 | if ( strlen(str)>0 ) |
---|
1209 | result = str |
---|
1210 | endif |
---|
1211 | return result |
---|
1212 | End |
---|
1213 | |
---|
1214 | |
---|
1215 | // ====================================== |
---|
1216 | //@+ |
---|
1217 | // .. index:: ! H5GW__AppendString() |
---|
1218 | // |
---|
1219 | // H5GW__AppendString(str, sep, newtext) |
---|
1220 | // ------------------------------------------------------------------------------------------------------------- |
---|
1221 | // |
---|
1222 | // :String str: starting string |
---|
1223 | // :String sep: separator |
---|
1224 | // :String newtext: text to be appended |
---|
1225 | // :returns String: result |
---|
1226 | //@- |
---|
1227 | |
---|
1228 | Static Function/T H5GW__AppendString(str, sep, newtext) |
---|
1229 | String str, sep, newtext |
---|
1230 | if ( strlen(newtext) == 0 ) |
---|
1231 | return str |
---|
1232 | endif |
---|
1233 | if ( strlen(str) > 0 ) |
---|
1234 | str = str + sep |
---|
1235 | endif |
---|
1236 | return str +newtext |
---|
1237 | End |
---|
1238 | |
---|
1239 | |
---|
1240 | |
---|
1241 | |
---|
1242 | // ====================================== |
---|
1243 | //@+ |
---|
1244 | // .. index:: ! H5GW__FileExists() |
---|
1245 | // |
---|
1246 | // H5GW__FileExists(file_name) |
---|
1247 | // ------------------------------------------------------------------------------------------------------------- |
---|
1248 | // |
---|
1249 | // :String file_name: name of file to be found |
---|
1250 | // :returns int: 1 if exists, 0 if does not exist |
---|
1251 | //@- |
---|
1252 | |
---|
1253 | Static Function H5GW__FileExists(file_name) |
---|
1254 | String file_name |
---|
1255 | Variable fileID |
---|
1256 | // Open/R/P=home/Z fileID as file_name // test if it will open as a regular file |
---|
1257 | Open/R/P=catPathName/Z fileID as file_name // test if it will open as a regular file |
---|
1258 | if ( fileID > 0 ) |
---|
1259 | Close fileID |
---|
1260 | return 1 |
---|
1261 | endif |
---|
1262 | return 0 |
---|
1263 | End |
---|
1264 | |
---|
1265 | |
---|
1266 | // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // |
---|
1267 | |
---|
1268 | |
---|
1269 | //@+ |
---|
1270 | // Testing and Development |
---|
1271 | // ====================================== |
---|
1272 | // |
---|
1273 | // Examples to test read and write: |
---|
1274 | // |
---|
1275 | // .. code-block:: guess |
---|
1276 | // :linenos: |
---|
1277 | // |
---|
1278 | // print H5GW_ReadHDF5("root:worker", "simpleexamplefile.h5") |
---|
1279 | // print H5GW_ReadHDF5("root:worker", "simple2dcase.h5") |
---|
1280 | // print H5GW_ReadHDF5("root:worker", "simple2dmaskedcase.h5") |
---|
1281 | // print H5GW_ReadHDF5("root:worker", "generic2dqtimeseries.h5") |
---|
1282 | // print H5GW_ReadHDF5("root:worker", "generic2dtimetpseries.h5") |
---|
1283 | // print H5GW_WriteHDF5("root:worker:simpleexamplefile", "test_output.h5") |
---|
1284 | // |
---|
1285 | // .. index:: test |
---|
1286 | // .. index:: ! H5GW__TestFile() |
---|
1287 | // |
---|
1288 | // H5GW__TestFile(parentDir, sourceFile) |
---|
1289 | // ------------------------------------------------------------------------------------------------------------- |
---|
1290 | // |
---|
1291 | // Reads HDF5 file sourceFile into a subfolder of IgorPro folder parentDir. |
---|
1292 | // Then writes the structure from that subfolder to a new HDF5 file: ``"test_"+sourceFile`` |
---|
1293 | // Assumes that sourceFile is only a file name, with no path components, in the present working directory. |
---|
1294 | // Returns the name (string) of the new HDF5 file written. |
---|
1295 | // |
---|
1296 | // :String parentDir: folder within IgorPro memory to contain the HDF5 test data |
---|
1297 | // :String sourceFile: HDF5 test data file (assumes no file path information prepends the file name) |
---|
1298 | //@- |
---|
1299 | |
---|
1300 | static Function/T H5GW__TestFile(parentDir, sourceFile) |
---|
1301 | String parentDir, sourceFile |
---|
1302 | String prefix = "test_" |
---|
1303 | String newFile = prefix + sourceFile |
---|
1304 | String name = StringFromList(0, sourceFile, ".") |
---|
1305 | print H5GW_ReadHDF5(parentDir, sourceFile) |
---|
1306 | print H5GW_WriteHDF5(parentDir+":"+name, newFile) |
---|
1307 | return newFile |
---|
1308 | End |
---|