source: sans/Dev/trunk/NCNR_User_Procedures/Reduction/HDF5gateway_NCNR.ipf @ 960

Last change on this file since 960 was 960, checked in by srkline, 7 years ago

more work with SANS and VSANS Nexus files, still trying to organize the r/w routines for just the basic testing of the first pass of NICE-generated files. Ditched the idea of generating the files from Igor, since DAS_log is not included. Now the plan is to start from a NICE/Nexus template. All still EXTREMELY fluid at this point.

File size: 44.0 KB
RevLine 
[946]1#pragma rtGlobals=3             // Use modern global access method.
2
3#include <HDF5 Browser>
4
[955]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
[946]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
[960]399Function/S H5GW_ReadHDF5(parentFolder, fileName, [hdf5Path])
[946]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
[960]416
417
[946]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       
[960]424s_tic()         //fast
425       
[946]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
[960]436
437s_toc()
438       
439//s_tic()               // this is the slow part, 0.7s for Igor-generated. > 9s for NICE (which has DAS_log)
440
[946]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
[960]454        KillStrings/Z file_path, objectPaths, group_name_list, dataset_name_list // ,file_name
[946]455        KillVariables/Z fileID
[960]456
457//s_toc()
[946]458       
459        SetDataFolder $oldFolder
460        return status
461End
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//@-
482Function/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
518End
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
540Function/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 ""
580End
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
599Function 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
619End
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//@-
643static 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
657End
658
659
660//@+
661//      .. index:: ! H5GW__WriteHDF5_Data()
662//     
663//      H5GW__WriteHDF5_Data(fileID)
664//      -------------------------------------------------------------------------------------------------------------
665//@-
666static 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
732End
733
734
735//@+
736//      .. index:: ! H5GW__SetHDF5ObjectAttributes()
737//     
738//      H5GW__SetHDF5ObjectAttributes(itemID, igorPath, hdf5Path)
739//      -------------------------------------------------------------------------------------------------------------
740//@-
741static 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
757End
758
759
760//@+
761//      .. index:: ! H5GW__SetTextAttributeHDF5()
762//     
763//      H5GW__SetTextAttributeHDF5(itemID, name, value, hdf5Path)
764//      -------------------------------------------------------------------------------------------------------------
765//@-
766static 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
778End
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
807static 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
[960]846
[946]847End
848
849
850//@+
851//      .. index:: ! H5GW__addPathXref()
852//     
853//      H5GW__addPathXref(parentFolder, base_name, hdf5Path, igorPath, xref, keySep, listSep)
854//      ----------------------------------------------------------------------------------------------------------------------------------------------------------
855//@-
856static 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
870End
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//@-
881static 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)
884End
885
886
887//@+
888//      .. index:: ! H5GW__appendPathDelimiter()
889//     
890//      H5GW__appendPathDelimiter(str, sep)
891//      -------------------------------------------------------------------------------------------------------------
892//@-
893static 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
899End
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
916static 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
928End
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
950Static 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
[955]960        HDF5OpenFile/R/P=home/Z fileID as fileName
961//      HDF5OpenFile/R/P=catPathName/Z fileID as fileName
[946]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
977End
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
1003Static 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       
[960]1011//s_tic()       // 0.11 s
[946]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
[960]1038
1039//s_toc()
1040//s_tic()       // 3.2 s !!
[946]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
[960]1056//s_toc()
1057//s_tic()       // worst @ 6.3 s
1058
[946]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
[960]1075//s_toc()
[946]1076
1077End
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
1103Static 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
1141End
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
1163Static 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
1189End
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
1204Static 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
1212End
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
1228Static 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
1237End
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
1253Static 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
1263End
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
1300static 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
1308End
Note: See TracBrowser for help on using the repository browser.