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

Last change on this file since 803 was 803, checked in by ilavsky, 3 years ago

Nika added Batch processing to speed up the code and fixed Geometry bug which caused recalculation of geometry waves all the time. Increase in speed 50-75%

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