source: trunk/User Procedures/CanSAS/HDF5gateway.ipf @ 938

Last change on this file since 938 was 938, checked in by ilavsky, 2 years ago

Igor 9 related changes

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