source: sans/Dev/trunk/NCNR_User_Procedures/Common/cansasXML_v11.ipf @ 430

Last change on this file since 430 was 403, checked in by ajj, 15 years ago

Adding reading of canSASXML data to Analysis package.

Notes:

  • Names datafolders from title of dataset to cope with multiple datasets per file, but there is no checking for collisions other than to pop up the "this has been loaded before" dialog.
  • I've put them in a "Common" folder as we should be moving to common data file handling.
File size: 36.1 KB
Line 
1#pragma rtGlobals=1             // Use modern global access method.
2#pragma version=1.10
3
4// file:        cansasXML.ipf
5// author:      Pete R. Jemian <jemian@anl.gov>
6// SVN date:    $Date: 2008-09-02 11:41:00 -0400 (Tue, 02 Sep 2008) $
7// SVN rev.:    $Revision: 60 $
8// SVN URL:     $HeadURL: http://svn.smallangles.net/svn/canSAS/1dwg/trunk/IgorPro/cansasXML.ipf $
9// SVN ID:      $Id: cansasXML.ipf 60 2008-09-02 15:41:00Z prjemian $
10// purpose:  implement an IgorPro file reader to read the canSAS 1-D reduced SAS data in XML files
11//                      adheres to the cansas1d/1.0 standard
12// readme:    http://www.smallangles.net/wgwiki/index.php/cansas1d_binding_IgorPro
13// URL: http://www.smallangles.net/wgwiki/index.php/cansas1d_documentation
14//
15// requires:    IgorPro (http://www.wavemetrics.com/)
16//                              XMLutils - XOP (http://www.igorexchange.com/project/XMLutils)
17// provides:  CS_CmlReader(String fileName)
18//                              all other functions in this file should not be relied upon
19
20// ==================================================================
21// CS_XmlReader("bimodal-test1.xml")
22// CS_XmlReader("1998spheres.xml")
23// CS_XmlReader("xg009036_001.xml")
24// CS_XmlReader("s81-polyurea.xml")
25// CS_XmlReader("cs_af1410.xml")
26//  testCollette();  prjTest_cansas1d()
27// ==================================================================
28
29
30#if( Exists("XmlOpenFile") )
31        // BEFORE we do anything else, check that XMLutils XOP is available.
32
33
34FUNCTION CS_XmlReader(fileName)
35        //
36        // open a canSAS 1-D reduced SAS XML data file
37        //      returns:
38        //              0 : successful
39        //              -1: XML file not found
40        //              -2: root element is not <SASroot> with valid canSAS namespace
41        //              -3: <SASroot> version  is not 1.0
42        //              -4: no <SASentry> elements
43        //              -5: XMLutils XOP needs upgrade
44        //              -6: XMLutils XOP not found
45        //
46        STRING fileName
47        STRING origFolder
48        STRING workingFolder = "root:Packages:CS_XMLreader"
49        VARIABLE returnCode
50
51
52        //
53        // set up a work folder within root:Packages
54        // Clear out any progress/results from previous activities
55        //
56        origFolder = GetDataFolder(1)
57        SetDataFolder root:                                     // start in the root data folder
58        NewDataFolder/O  root:Packages          // good practice
59        KillDataFolder/Z  $workingFolder                // clear out any previous work
60        NewDataFolder/O/S  $workingFolder       // Do all our work in root:XMLreader
61
62        //
63        // Try to open the named XML file (clean-up and return if failure)
64        //
65        VARIABLE fileID
66        STRING/G errorMsg, xmlFile
67        xmlFile = fileName
68        fileID = XmlOpenFile(fileName)                  // open and parse the XMLfile
69        IF ( fileID < 0 )
70                SWITCH(fileID)                                  // fileID holds the return code; check it
71                        CASE -1:
72                                errorMsg = fileName + ": failed to parse XML"
73                        BREAK
74                        CASE -2:
75                                errorMsg = fileName + " either not found or cannot be opened for reading"
76                        BREAK
77                ENDSWITCH
78                PRINT errorMsg
79                SetDataFolder $origFolder
80                RETURN(-1)                                              // could not find file
81        ENDIF
82
83        //
84        //      test to see if XMLutils has the needed upgrade
85        //
86        XMLlistXpath(fileID, "/*", "") 
87        IF ( EXISTS( "M_listXPath" ) == 0 )
88                XmlCloseFile(fileID,0)
89                errorMsg = "XMLutils needs an upgrade:  http://www.igorexchange.com/project/XMLutils"
90                PRINT errorMsg
91                SetDataFolder $origFolder
92                RETURN(-5)                                              // XOPutils needs an upgrade
93        ENDIF
94        WAVE/T  M_listXPath
95
96        // check for canSAS namespace string, returns "" if not valid or not found
97        STRING/G ns = CS_getDefaultNamespace(fileID)
98        IF (strlen(ns) == 0 )
99                XmlCloseFile(fileID,0)
100                errorMsg = "root element is not <SASroot> with valid canSAS namespace"
101                PRINT errorMsg
102                SetDataFolder $origFolder
103                RETURN(-2)                                              // root element is not <SASroot> with valid canSAS namespace
104        ENDIF
105        STRING/G nsPre = "cs:"
106        STRING/G nsStr = "cs=" + ns
107
108        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
109        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
110       
111        STRSWITCH(ns)   
112        CASE "cansas1d/1.0":                                                    // version 1.0 of the canSAS 1-D reduced SAS data standard
113                PRINT fileName, "\t\t identified as: cansas1d/1.0 XML file"
114                returnCode = CS_1i_parseXml(fileID)                     //  This is where the action happens!
115                IF (returnCode != 0)
116                        IF (strlen(errorMsg) == 0)
117                                errorMsg = "error while parsing the XML"
118                        ENDIF
119                        PRINT errorMsg
120                        XmlCloseFile(fileID,0)
121                        SetDataFolder $origFolder
122                        RETURN(returnCode)                      // error while parsing the XML
123                ENDIF
124                BREAK
125        CASE "cansas1d/2.0a":                                           // unsupported
126        DEFAULT:                                                        // optional default expression executed
127                errorMsg = fileName + ": <SASroot>, namespace (" + ns + ") is not supported"
128                PRINT errorMsg
129                XmlCloseFile(fileID,0)
130                SetDataFolder $origFolder
131                RETURN(-3)                                              // attribute list must include version="1.0"
132        ENDSWITCH
133
134        XmlCloseFile(fileID,0)                                  // now close the file, without saving
135        fileID = -1
136
137        SetDataFolder root:Packages:CS_XMLreader
138        KillWaves/Z M_listXPath, SASentryList
139        SetDataFolder $origFolder
140        RETURN(0)                                                       // execution finished OK
141END
142
143FUNCTION/S CS_getDefaultNamespace(fileID)
144        // Test here (by guessing) for the various known namespaces.
145        // Return the one found in the "schemaLocation" attribute
146        // since the XMLutils XOP does not provide any xmlns attributes.
147        // It is possible to call XMLelemList and get the namespace directly
148        // but that call can be expensive (time) when there are lots of elements.
149        VARIABLE fileID
150        STRING ns = "", thisLocation
151        VARIABLE i, item
152        MAKE/T/N=(1)/O nsList           // list of all possible namespaces
153        nsList[0] = "cansas1d/1.0"              // first version of canSAS 1-D reduced SAS
154
155        FOR (item = 0; item < DimSize(nsList, 0); item += 1)            // loop over all possible namespaces
156                XMLlistAttr(fileID, "/cs:SASroot", "cs="+nsList[item])
157                WAVE/T M_listAttr
158                FOR (i = 0; i < DimSize(M_listAttr,0); i+=1)                    // loop over all available attributes
159                        // Expect the required canSAS XML header (will fail if "schemalocation" is not found)
160                        IF ( CmpStr(  LowerStr(M_listAttr[i][1]),  LowerStr("schemaLocation") ) == 0 )
161                                thisLocation = TrimWS(M_listAttr[i][2])
162                                IF ( StringMatch(thisLocation, nsList[item] + "*") )
163                                        ns = nsList[item]
164                                        BREAK           // found it!
165                                ENDIF
166                        ENDIF
167                ENDFOR
168                IF (strlen(ns))
169                        BREAK           // found it!
170                ENDIF
171        ENDFOR
172
173        KillWaves/Z nsList, M_listAttr
174        RETURN ns
175END
176
177// ==================================================================
178
179FUNCTION CS_1i_parseXml(fileID)
180        VARIABLE fileID
181        SVAR errorMsg, xmlFile
182        STRING/G Title, Title_folder
183        VARIABLE i, j, index, SASdata_index, returnCode = 0
184
185        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
186        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
187
188        // locate all the SASentry elements
189        //      assume nsPre = "cs" otherwise
190        // "/"+nsPre+":SASroot//"+nsPre+":SASentry"
191        XmlListXpath(fileID, "/cs:SASroot//cs:SASentry", nsStr)
192        WAVE/T  M_listXPath
193        STRING          SASentryPath
194        DUPLICATE/O/T   M_listXPath, SASentryList
195
196        FOR (i=0; i < DimSize(SASentryList, 0); i += 1)
197                SASentryPath = "/cs:SASroot/cs:SASentry["+num2str(i+1)+"]"
198                SetDataFolder root:Packages:CS_XMLreader
199               
200                title =  CS_1i_locateTitle(fileID, SASentryPath)
201                Title_folder = CS_cleanFolderName(Title)
202                NewDataFolder/O/S  $Title_folder
203
204                XmlListXpath(fileID, SASentryPath + "//cs:SASdata", nsStr)
205                WAVE/T  M_listXPath
206                IF ( DimSize(M_listXPath, 0) == 1)
207                        CS_1i_getOneSASdata(fileID, Title, SASentryPath+"/cs:SASdata")
208                        CS_1i_collectMetadata(fileID, SASentryPath)
209                ELSE
210                        FOR (j = 0; j < DimSize(M_listXPath, 0); j += 1)
211                                STRING SASdataFolder = CS_cleanFolderName("SASdata_" + num2str(j))
212                                NewDataFolder/O/S  $SASdataFolder
213                                CS_1i_getOneSASdata(fileID, Title, SASentryPath+"/cs:SASdata["+num2str(j+1)+"]")
214                                CS_1i_collectMetadata(fileID, SASentryPath)
215                                SetDataFolder ::                        // back up to parent directory
216                        ENDFOR
217                ENDIF
218                KillWaves/Z M_listXPath
219        ENDFOR
220
221        SetDataFolder root:Packages:CS_XMLreader
222        KillWaves/Z M_listXPath, SASentryList
223        RETURN(returnCode)
224END
225
226// ==================================================================
227
228FUNCTION/S CS_cleanFolderName(proposal)
229        STRING proposal
230        STRING result
231        result = CleanupName(proposal, 0)
232        IF ( CheckName(result, 11) != 0 )
233                result = UniqueName(result, 11, 0)
234        ENDIF
235        RETURN result
236END
237
238// ==================================================================
239
240FUNCTION CS_1i_getOneSASdata(fileID, Title, SASdataPath)
241        VARIABLE fileID
242        STRING Title, SASdataPath
243        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
244        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
245        VARIABLE i
246        STRING SASdata_name, suffix = ""
247
248        //grab the data and put it in the working data folder
249        CS_1i_GetReducedSASdata(fileID, SASdataPath)
250
251        //start the metadata
252        MAKE/O/T/N=(0,2) metadata
253
254        SVAR xmlFile = root:Packages:CS_XMLreader:xmlFile
255        CS_appendMetaData(fileID, "xmlFile", "", xmlFile)
256
257        SVAR ns = root:Packages:CS_XMLreader:ns
258        CS_appendMetaData(fileID, "namespace", "", ns)
259        CS_appendMetaData(fileID, "Title", "", Title)
260       
261        XmlListXpath(fileID, SASdataPath + "/..//cs:Run", nsStr)
262        WAVE/T  M_listXPath
263        FOR (i=0; i < DimSize(M_listXPath, 0); i += 1)
264                IF ( DimSize(M_listXPath, 0) > 1 )
265                        suffix = "_" + num2str(i)
266                ENDIF
267                CS_appendMetaData(fileID, "Run" + suffix,  SASdataPath + "/../cs:Run["+num2str(i+1)+"]", "")
268                CS_appendMetaData(fileID, "Run/@name" + suffix,  SASdataPath + "/../cs:Run["+num2str(i+1)+"]/@name", "")
269        ENDFOR
270
271        SASdata_name = TrimWS(XMLstrFmXpath(fileID,  SASdataPath + "/@name", nsStr, ""))
272        CS_appendMetaData(fileID, "SASdata/@name", "", SASdata_name)
273
274        KillWaves/Z M_listXPath
275END
276
277// ==================================================================
278
279FUNCTION CS_1i_getOneVector(file,prefix,XML_name,Igor_name)
280        VARIABLE file
281        STRING prefix,XML_name,Igor_name
282        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
283        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
284
285        XmlWaveFmXpath(file,prefix+XML_name,nsStr,"")                   //this loads ALL the vector's nodes at the same time
286        WAVE/T M_xmlcontent
287        WAVE/T W_xmlContentNodes
288        IF (DimSize(M_xmlcontent, 0))                   // test to see if the nodes exist.  not strictly necessary if you know the nodes are there
289                IF (DimSize(M_xmlcontent,1)>DimSize(M_xmlcontent,0))    //if you're not in vector mode
290                        MatrixTranspose M_xmlcontent
291                ENDIF
292                MAKE/O/D/N=(DimSize(M_xmlcontent, 0)) $Igor_name
293                WAVE vect = $Igor_name
294                vect=str2num(M_xmlcontent)
295        ENDIF
296        KILLWAVES/Z M_xmlcontent, W_xmlContentNodes
297END
298
299// ==================================================================
300
301FUNCTION CS_1i_GetReducedSASdata(fileID, SASdataPath)
302        VARIABLE fileID
303        STRING SASdataPath
304        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
305        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
306        STRING prefix = ""
307        VARIABLE pos
308
309        VARIABLE cansasStrict = 1               // !!!software developer's choice!!!
310        IF (cansasStrict)               // only get known canSAS data vectors
311                prefix = SASdataPath + "//cs:"
312                // load ALL nodes of each vector (if exists) at the same time
313                CS_1i_getOneVector(fileID, prefix, "Q",                 "Qsas")
314                CS_1i_getOneVector(fileID, prefix, "I",                 "Isas")
315                CS_1i_getOneVector(fileID, prefix, "Idev",              "Idev")
316                CS_1i_getOneVector(fileID, prefix, "Qdev",              "Qdev")
317                CS_1i_getOneVector(fileID, prefix, "dQw",       "dQw")
318                CS_1i_getOneVector(fileID, prefix, "dQl",               "dQl")
319                CS_1i_getOneVector(fileID, prefix, "Qmean",     "Qmean")
320                CS_1i_getOneVector(fileID, prefix, "Shadowfactor",      "Shadowfactor")
321                // check them for common length
322        ELSE                            // search for _ANY_ data vectors
323                // find the names of all the data columns and load them as vectors
324                // this gets tricky if we want to avoid namespace references
325                XmlListXpath(fileID, SASdataPath+"//cs:Idata[1]/*", nsStr)
326                WAVE/T M_listXPath
327                STRING xmlElement, xPathStr
328                STRING igorWave
329                VARIABLE j
330                FOR (j = 0; j < DimSize(M_listXPath, 0); j += 1)        // loop over all columns in SASdata/Idata[1]
331                        xmlElement = M_listXPath[j][1]
332                        STRSWITCH(xmlElement)
333                                CASE "Q":               // IgorPro does not allow a variable named Q
334                                CASE "I":                       // or I
335                                        igorWave = xmlElement + "sas"
336                                        BREAK
337                                DEFAULT:
338                                        igorWave = xmlElement           // can we trust this one?
339                        ENDSWITCH
340                        //
341//                      // This will need some work to support foreign namespaces here
342//                      //
343//                      //
344//                      xPathStr = M_listXPath[j][0]                                                    // clear name reference
345//                      pos = strsearch(xPathStr, "/", Inf, 3)                                  // peel off the tail of the string and reform
346//                      xmlElement = xPathStr[pos,Inf]                                          // find last element on the path
347//                      prefix = xPathStr[0, pos-1-4]+"/*"                                              // ALL Idata elements
348//                      CS_1i_getOneVector(fileID,prefix, xmlElement, igorWave)         // loads ALL rows (Idata) of the column at the same time
349                        //
350                        //  Could there be a problem with a foreign namespace here?
351                        prefix = SASdataPath+"//cs:Idata"                                               // ALL Idata elements
352                        xmlElement = "cs:" + M_listXPath[j][1]                                  // just this column
353                        CS_1i_getOneVector(fileID,prefix, xmlElement, igorWave)         // loads ALL rows (Idata) of the column at the same time
354                ENDFOR
355                // check them for common length
356        ENDIF
357 
358        //get rid of any mess
359        KILLWAVES/z M_listXPath
360END
361
362// ==================================================================
363
364FUNCTION CS_1i_collectMetadata(fileID, sasEntryPath)
365        VARIABLE fileID
366        STRING sasEntryPath
367        VARIABLE i, j
368        WAVE/T metadata
369        STRING suffix = "", preMeta = "", preXpath = ""
370        STRING value, detailsPath, detectorPath, notePath
371
372        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
373        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
374
375        // collect some metadata
376        // first, fill a table with keywords, and XPath locations, 3rd column will be values
377
378        // handle most <SASsample> fields
379        CS_appendMetaData(fileID, "SASsample/@name",                            sasEntryPath + "/cs:SASsample/@name", "")
380        CS_appendMetaData(fileID, "SASsample/ID",                                       sasEntryPath + "/cs:SASsample/cs:ID", "")
381        CS_appendMetaData(fileID, "SASsample/thickness",                                sasEntryPath + "/cs:SASsample/cs:thickness", "")
382        CS_appendMetaData(fileID, "SASsample/thickness/@unit",                  sasEntryPath + "/cs:SASsample/cs:thickness/@unit", "")
383        CS_appendMetaData(fileID, "SASsample/transmission",                     sasEntryPath + "/cs:SASsample/cs:transmission", "")
384        CS_appendMetaData(fileID, "SASsample/temperature",                      sasEntryPath + "/cs:SASsample/cs:temperature", "")
385        CS_appendMetaData(fileID, "SASsample/temperature/@unit",           sasEntryPath + "/cs:SASsample/cs:temperature/@unit", "")
386        CS_appendMetaData(fileID, "SASsample/position/x",                          sasEntryPath + "/cs:SASsample/cs:position/cs:x", "")
387        CS_appendMetaData(fileID, "SASsample/position/x/@unit",            sasEntryPath + "/cs:SASsample/cs:position/cs:x/@unit", "")
388        CS_appendMetaData(fileID, "SASsample/position/y",                          sasEntryPath + "/cs:SASsample/cs:position/cs:y", "")
389        CS_appendMetaData(fileID, "SASsample/position/y/@unit",            sasEntryPath + "/cs:SASsample/cs:position/cs:y/@unit", "")
390        CS_appendMetaData(fileID, "SASsample/position/z",                          sasEntryPath + "/cs:SASsample/cs:position/cs:z", "")
391        CS_appendMetaData(fileID, "SASsample/position/z/@unit",            sasEntryPath + "/cs:SASsample/cs:position/cs:z/@unit", "")
392        CS_appendMetaData(fileID, "SASsample/orientation/roll",                    sasEntryPath + "/cs:SASsample/cs:orientation/cs:roll", "")
393        CS_appendMetaData(fileID, "SASsample/orientation/roll/@unit",      sasEntryPath + "/cs:SASsample/cs:orientation/cs:roll/@unit", "")
394        CS_appendMetaData(fileID, "SASsample/orientation/pitch",           sasEntryPath + "/cs:SASsample/cs:orientation/cs:pitch", "")
395        CS_appendMetaData(fileID, "SASsample/orientation/pitch/@unit",     sasEntryPath + "/cs:SASsample/cs:orientation/cs:pitch/@unit", "")
396        CS_appendMetaData(fileID, "SASsample/orientation/yaw",                     sasEntryPath + "/cs:SASsample/cs:orientation/cs:yaw", "")
397        CS_appendMetaData(fileID, "SASsample/orientation/yaw/@unit",       sasEntryPath + "/cs:SASsample/cs:orientation/cs:yaw/@unit", "")
398        // <SASsample><details> might appear multiple times, too!
399        XmlListXpath(fileID, sasEntryPath+"/cs:SASsample//cs:details", nsStr)   //output: M_listXPath
400        WAVE/T  M_listXPath
401        DUPLICATE/O/T   M_listXPath, detailsList
402        suffix = ""
403        FOR (i = 0; i < DimSize(detailsList, 0); i += 1)
404                IF (DimSize(detailsList, 0) > 1)
405                        suffix = "_" + num2str(i)
406                ENDIF
407                detailsPath = sasEntryPath+"/cs:SASsample/cs:details["+num2str(i+1)+"]"
408                CS_appendMetaData(fileID, "SASsample/details"+suffix+"/@name",  detailsPath + "/@name", "")
409                CS_appendMetaData(fileID, "SASsample/details"+suffix,           detailsPath, "")
410        ENDFOR
411
412
413        // <SASinstrument>
414        CS_appendMetaData(fileID, "SASinstrument/name",         sasEntryPath + "/cs:SASinstrument/cs:name", "")
415        CS_appendMetaData(fileID, "SASinstrument/@name",        sasEntryPath + "/cs:SASinstrument/@name", "")
416
417        // <SASinstrument><SASsource>
418        preMeta = "SASinstrument/SASsource"
419        preXpath = sasEntryPath + "/cs:SASinstrument/cs:SASsource"
420        CS_appendMetaData(fileID, preMeta + "/@name",                      preXpath + "/@name", "")
421        CS_appendMetaData(fileID, preMeta + "/radiation",                  preXpath + "/cs:radiation", "")
422        CS_appendMetaData(fileID, preMeta + "/beam/size/@name",            preXpath + "/cs:beam_size/@name", "")
423        CS_appendMetaData(fileID, preMeta + "/beam/size/x",                preXpath + "/cs:beam_size/cs:x", "")
424        CS_appendMetaData(fileID, preMeta + "/beam/size/x@unit",           preXpath + "/cs:beam_size/cs:x/@unit", "")
425        CS_appendMetaData(fileID, preMeta + "/beam/size/y",                preXpath + "/cs:beam_size/cs:y", "")
426        CS_appendMetaData(fileID, preMeta + "/beam/size/y@unit",           preXpath + "/cs:beam_size/cs:y/@unit", "")
427        CS_appendMetaData(fileID, preMeta + "/beam/size/z",                preXpath + "/cs:beam_size/cs:z", "")
428        CS_appendMetaData(fileID, preMeta + "/beam/size/z@unit",           preXpath + "/cs:beam_size/cs:z/@unit", "")
429        CS_appendMetaData(fileID, preMeta + "/beam/shape",                 preXpath + "/cs:beam_shape", "")
430        CS_appendMetaData(fileID, preMeta + "/wavelength",                 preXpath + "/cs:wavelength", "")
431        CS_appendMetaData(fileID, preMeta + "/wavelength/@unit",           preXpath + "/cs:wavelength/@unit", "")
432        CS_appendMetaData(fileID, preMeta + "/wavelength_min",             preXpath + "/cs:wavelength_min", "")
433        CS_appendMetaData(fileID, preMeta + "/wavelength_min/@unit",       preXpath + "/cs:wavelength_min/@unit", "")
434        CS_appendMetaData(fileID, preMeta + "/wavelength_max",             preXpath + "/cs:wavelength_max", "")
435        CS_appendMetaData(fileID, preMeta + "/wavelength_max/@unit",       preXpath + "/cs:wavelength_max/@unit", "")
436        CS_appendMetaData(fileID, preMeta + "/wavelength_spread",          preXpath + "/cs:wavelength_spread", "")
437        CS_appendMetaData(fileID, preMeta + "/wavelength_spread/@unit",    preXpath + "/cs:wavelength_spread/@unit", "")
438
439        // <SASinstrument><SAScollimation> might appear multiple times
440        XmlListXpath(fileID, sasEntryPath+"/cs:SASinstrument//cs:SAScollimation", nsStr)        //output: M_listXPath
441        WAVE/T  M_listXPath
442        DUPLICATE/O/T   M_listXPath, SAScollimationList
443        STRING collimationPath
444        FOR (i = 0; i < DimSize(SAScollimationList, 0); i += 1)
445                preMeta = "SASinstrument/SAScollimation"
446                IF (DimSize(SAScollimationList, 0) > 1)
447                        preMeta += "_" + num2str(i)
448                ENDIF
449                collimationPath = sasEntryPath+"/cs:SASinstrument/cs:SAScollimation["+num2str(i+1)+"]"
450                CS_appendMetaData(fileID, preMeta + "/@name",               collimationPath + "/@name", "")
451                CS_appendMetaData(fileID, preMeta + "/length",              collimationPath + "/cs:length", "")
452                CS_appendMetaData(fileID, preMeta + "/length_unit",         collimationPath + "/cs:length/@unit", "")
453                FOR (j = 0; j < DimSize(M_listXPath, 0); j += 1)        // aperture may be repeated!
454                        IF (DimSize(M_listXPath, 0) == 1)
455                                preMeta = "SASinstrument/SAScollimation/aperture"
456                        ELSE
457                                preMeta = "SASinstrument/SAScollimation/aperture_" + num2str(j)
458                        ENDIF
459                        preXpath = collimationPath + "/cs:aperture["+num2str(j+1)+"]"
460                        CS_appendMetaData(fileID, preMeta + "/@name",         preXpath + "/@name", "")
461                        CS_appendMetaData(fileID, preMeta + "/type",          preXpath + "/cs:type", "")
462                        CS_appendMetaData(fileID, preMeta + "/size/@name",     preXpath + "/cs:size/@name", "")
463                        CS_appendMetaData(fileID, preMeta + "/size/x",        preXpath + "/cs:size/cs:x", "")
464                        CS_appendMetaData(fileID, preMeta + "/size/x/@unit",   preXpath + "/cs:size/cs:x/@unit", "")
465                        CS_appendMetaData(fileID, preMeta + "/size/y",        preXpath + "/cs:size/cs:y", "")
466                        CS_appendMetaData(fileID, preMeta + "/size/y/@unit",   preXpath + "/cs:size/cs:y/@unit", "")
467                        CS_appendMetaData(fileID, preMeta + "/size/z",        preXpath + "/cs:size/cs:z", "")
468                        CS_appendMetaData(fileID, preMeta + "/size/z/@unit",   preXpath + "/cs:size/cs:z/@unit", "")
469                        CS_appendMetaData(fileID, preMeta + "/distance",       preXpath + "/cs:distance", "")
470                        CS_appendMetaData(fileID, preMeta + "/distance/@unit", preXpath + "/cs:distance/@unit", "")
471                ENDFOR
472        ENDFOR
473
474        // <SASinstrument><SASdetector> might appear multiple times
475        XmlListXpath(fileID, sasEntryPath+"/cs:SASinstrument//cs:SASdetector", nsStr)   //output: M_listXPath
476        WAVE/T  M_listXPath
477        DUPLICATE/O/T   M_listXPath, SASdetectorList
478        FOR (i = 0; i < DimSize(SASdetectorList, 0); i += 1)
479                preMeta = "SASinstrument/SASdetector"
480                IF (DimSize(SASdetectorList, 0) > 1)
481                        preMeta += "_" + num2str(i)
482                ENDIF
483                detectorPath = sasEntryPath+"/cs:SASinstrument/cs:SASdetector["+num2str(i+1)+"]"
484                CS_appendMetaData(fileID, preMeta + "/@name",                    detectorPath + "/cs:name", "")
485                CS_appendMetaData(fileID, preMeta + "/SDD",                              detectorPath + "/cs:SDD", "")
486                CS_appendMetaData(fileID, preMeta + "/SDD/@unit",                        detectorPath + "/cs:SDD/@unit", "")
487                CS_appendMetaData(fileID, preMeta + "/offset/@name",             detectorPath + "/cs:offset/@name", "")
488                CS_appendMetaData(fileID, preMeta + "/offset/x",                 detectorPath + "/cs:offset/cs:x", "")
489                CS_appendMetaData(fileID, preMeta + "/offset/x/@unit",           detectorPath + "/cs:offset/cs:x/@unit", "")
490                CS_appendMetaData(fileID, preMeta + "/offset/y",                 detectorPath + "/cs:offset/cs:y", "")
491                CS_appendMetaData(fileID, preMeta + "/offset/y/@unit",           detectorPath + "/cs:offset/cs:y/@unit", "")
492                CS_appendMetaData(fileID, preMeta + "/offset/z",                 detectorPath + "/cs:offset/cs:z", "")
493                CS_appendMetaData(fileID, preMeta + "/offset/z/@unit",           detectorPath + "/cs:offset/cs:z/@unit", "")
494
495                CS_appendMetaData(fileID, preMeta + "/orientation/@name",        detectorPath + "/cs:orientation/@name", "")
496                CS_appendMetaData(fileID, preMeta + "/orientation/roll",         detectorPath + "/cs:orientation/cs:roll", "")
497                CS_appendMetaData(fileID, preMeta + "/orientation/roll/@unit",   detectorPath + "/cs:orientation/cs:roll/@unit", "")
498                CS_appendMetaData(fileID, preMeta + "/orientation/pitch",        detectorPath + "/cs:orientation/cs:pitch", "")
499                CS_appendMetaData(fileID, preMeta + "/orientation/pitch/@unit",   detectorPath + "/cs:orientation/cs:pitch/@unit", "")
500                CS_appendMetaData(fileID, preMeta + "/orientation/yaw",          detectorPath + "/cs:orientation/cs:yaw", "")
501                CS_appendMetaData(fileID, preMeta + "/orientation/yaw/@unit",    detectorPath + "/cs:orientation/cs:yaw/@unit", "")
502
503                CS_appendMetaData(fileID, preMeta + "/beam_center/@name",        detectorPath + "/cs:beam_center/@name", "")
504                CS_appendMetaData(fileID, preMeta + "/beam_center/x",            detectorPath + "/cs:beam_center/cs:x", "")
505                CS_appendMetaData(fileID, preMeta + "/beam_center/x/@unit",      detectorPath + "/cs:beam_center/cs:x/@unit", "")
506                CS_appendMetaData(fileID, preMeta + "/beam_center/y",            detectorPath + "/cs:beam_center/cs:y", "")
507                CS_appendMetaData(fileID, preMeta + "/beam_center/y/@unit",      detectorPath + "/cs:beam_center/cs:y/@unit", "")
508                CS_appendMetaData(fileID, preMeta + "/beam_center/z",            detectorPath + "/cs:beam_center/cs:z", "")
509                CS_appendMetaData(fileID, preMeta + "/beam_center/z/@unit",      detectorPath + "/cs:beam_center/cs:z/@unit", "")
510
511                CS_appendMetaData(fileID, preMeta + "/pixel_size/@name",         detectorPath + "/cs:pixel_size/@name", "")
512                CS_appendMetaData(fileID, preMeta + "/pixel_size/x",             detectorPath + "/cs:pixel_size/cs:x", "")
513                CS_appendMetaData(fileID, preMeta + "/pixel_size/x/@unit",       detectorPath + "/cs:pixel_size/cs:x/@unit", "")
514                CS_appendMetaData(fileID, preMeta + "/pixel_size/y",             detectorPath + "/cs:pixel_size/cs:y", "")
515                CS_appendMetaData(fileID, preMeta + "/pixel_size/y/@unit",       detectorPath + "/cs:pixel_size/cs:y/@unit", "")
516                CS_appendMetaData(fileID, preMeta + "/pixel_size/z",             detectorPath + "/cs:pixel_size/cs:z", "")
517                CS_appendMetaData(fileID, preMeta + "/pixel_size/z/@unit",       detectorPath + "/cs:pixel_size/cs:z/@unit", "")
518
519                CS_appendMetaData(fileID, preMeta + "/slit_length",                    detectorPath + "/cs:slit_length", "")
520                CS_appendMetaData(fileID, preMeta + "/slit_length/@unit",              detectorPath + "/cs:slit_length/@unit", "")
521        ENDFOR
522
523        // <SASprocess> might appear multiple times
524        XmlListXpath(fileID, sasEntryPath+"//cs:SASprocess", nsStr)     //output: M_listXPath
525        WAVE/T  M_listXPath
526        DUPLICATE/O/T   M_listXPath, SASprocessList
527        STRING SASprocessPath, prefix
528        FOR (i = 0; i < DimSize(SASprocessList, 0); i += 1)
529                preMeta = "SASprocess"
530                IF (DimSize(SASprocessList, 0) > 1)
531                        preMeta += "_" + num2str(i)
532                ENDIF
533                SASprocessPath = sasEntryPath+"/cs:SASprocess["+num2str(i+1)+"]"
534                CS_appendMetaData(fileID, preMeta+"/@name",        SASprocessPath + "/@name", "")
535                CS_appendMetaData(fileID, preMeta+"/name",         SASprocessPath + "/cs:name", "")
536                CS_appendMetaData(fileID, preMeta+"/date",                 SASprocessPath + "/cs:date", "")
537                CS_appendMetaData(fileID, preMeta+"/description",   SASprocessPath + "/cs:description", "")
538                XmlListXpath(fileID, SASprocessPath+"//cs:term", nsStr)
539                FOR (j = 0; j < DimSize(M_listXPath, 0); j += 1)
540                        prefix = SASprocessPath + "/cs:term[" + num2str(j+1) + "]"
541                        CS_appendMetaData(fileID, preMeta+"/term_"+num2str(j)+"/@name",     prefix + "/@name", "")
542                        CS_appendMetaData(fileID, preMeta+"/term_"+num2str(j)+"/@unit",           prefix + "/@unit", "")
543                        CS_appendMetaData(fileID, preMeta+"/term_"+num2str(j),                            prefix, "")
544                ENDFOR
545                // ignore <SASprocessnote>
546        ENDFOR
547
548        // <SASnote> might appear multiple times
549        XmlListXpath(fileID, sasEntryPath+"//cs:SASnote", nsStr)        //output: M_listXPath
550        WAVE/T  M_listXPath
551        DUPLICATE/O/T   M_listXPath, SASnoteList
552        FOR (i = 0; i < DimSize(SASnoteList, 0); i += 1)
553                preMeta = "SASnote"
554                IF (DimSize(SASnoteList, 0) > 1)
555                        preMeta += "_" + num2str(i)
556                ENDIF
557                notePath = sasEntryPath+"//cs:SASnote["+num2str(i+1)+"]"
558                CS_appendMetaData(fileID, preMeta+"/@name",     notePath + "/@name", "")
559                CS_appendMetaData(fileID, preMeta,              notePath, "")
560        ENDFOR
561
562        KillWaves/Z M_listXPath, detailsList, SAScollimationList, SASdetectorList, SASprocessList, SASnoteList
563END
564
565// ==================================================================
566
567FUNCTION/S CS_1i_locateTitle(fileID, SASentryPath)
568        VARIABLE fileID
569        STRING SASentryPath
570        STRING TitlePath, Title
571        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
572        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
573
574        // /cs:SASroot/cs:SASentry/cs:Title is the expected location, but it could be empty
575        TitlePath = SASentryPath + "/cs:Title"
576        Title = XMLstrFmXpath(fileID,  TitlePath, nsStr, "")
577        // search harder for a title
578        IF (strlen(Title) == 0)
579                TitlePath = SASentryPath + "/@name"
580                Title = XMLstrFmXpath(fileID,  TitlePath, nsStr, "")
581        ENDIF
582        IF (strlen(Title) == 0)
583                TitlePath = SASentryPath + "/cs:SASsample/cs:ID"
584                Title = XMLstrFmXpath(fileID,  TitlePath, nsStr, "")
585        ENDIF
586        IF (strlen(Title) == 0)
587                TitlePath = SASentryPath + "/cs:SASsample/@name"
588                Title = XMLstrFmXpath(fileID,  TitlePath, nsStr, "")
589        ENDIF
590        IF (strlen(Title) == 0)
591                // last resort: make up a title
592                Title = "SASentry"
593                TitlePath = ""
594        ENDIF
595        PRINT "\t Title:", Title
596        RETURN(Title)
597END
598
599// ==================================================================
600
601FUNCTION CS_appendMetaData(fileID, key, xpath, value)
602        VARIABLE fileID
603        STRING key, xpath, value
604        WAVE/T metadata
605        STRING k, v
606
607        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
608        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
609
610        k = TrimWS(key)
611        IF (  strlen(k) > 0 )
612                IF ( strlen(xpath) > 0 )
613                        value = XMLstrFmXpath(fileID,  xpath, nsStr, "")
614                ENDIF
615                // What if the value string has a ";" embedded?
616                //  This could complicate (?compromise?) the wavenote "key=value;" syntax.
617                //  But let the caller deal with it.
618                v = TrimWS(ReplaceString(";", value, " :semicolon: "))
619                IF ( strlen(v) > 0 )
620                        VARIABLE last
621                        last = DimSize(metadata, 0)
622                        Redimension/N=(last+1, 2) metadata
623                        metadata[last][0] = k
624                        metadata[last][1] = v
625                ENDIF
626        ENDIF
627END
628
629// ==================================================================
630
631Function/T   TrimWS(str)
632    // TrimWhiteSpace (code from Jon Tischler)
633    String str
634    return TrimWSL(TrimWSR(str))
635End
636
637// ==================================================================
638
639Function/T   TrimWSL(str)
640    // TrimWhiteSpaceLeft (code from Jon Tischler)
641    String str
642    Variable i, N=strlen(str)
643    for (i=0;char2num(str[i])<=32 && i<N;i+=1)    // find first non-white space
644    endfor
645    return str[i,Inf]
646End
647
648// ==================================================================
649
650Function/T   TrimWSR(str)
651    // TrimWhiteSpaceRight (code from Jon Tischler)
652    String str
653    Variable i
654    for (i=strlen(str)-1; char2num(str[i])<=32 && i>=0; i-=1)    // find last non-white space
655    endfor
656    return str[0,i]
657End
658
659// ==================================================================
660// ==================================================================
661// ==================================================================
662
663
664FUNCTION prj_grabMyXmlData()
665        STRING srcDir = "root:Packages:CS_XMLreader"
666        STRING destDir = "root:PRJ_canSAS"
667        STRING srcFolder, destFolder, theFolder
668        Variable i
669        NewDataFolder/O  $destDir               // for all my imported data
670        FOR ( i = 0; i < CountObjects(srcDir, 4) ; i += 1 )
671                theFolder = GetIndexedObjName(srcDir, 4, i)
672                srcFolder = srcDir + ":" + theFolder
673                destFolder = destDir + ":" + theFolder
674                // PRINT srcFolder, destFolder
675                IF (DataFolderExists(destFolder))
676                        // !!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
677                        // need to find unique name for destination
678                        // Persons who implement this properly should be more elegant
679                        // For now, I will blast the existing and proceed blindly.
680                        KillDataFolder/Z  $destFolder           // clear out any previous work
681                        DuplicateDataFolder $srcFolder, $destFolder
682                ELSE
683                        DuplicateDataFolder $srcFolder, $destFolder
684                ENDIF
685        ENDFOR
686END
687
688FUNCTION prjTest_cansas1d()
689        // unit tests for the routines under prj-readXML.ipf
690        STRING theFile
691        STRING fList = ""
692        VARIABLE i, result, timerID, seconds
693        // build a table of test data sets
694        fList = AddListItem("elmo.xml",                                 fList, ";", Inf)                // non-existent file
695        fList = AddListItem("cansasXML.ipf",                    fList, ";", Inf)                // this file (should fail on XML parsing)
696        fList = AddListItem("book.xml",                                 fList, ";", Inf)                // good XML example file but not canSAS, not even close
697        fList = AddListItem("bimodal-test1.xml",                fList, ";", Inf)                // simple dataset
698        fList = AddListItem("bimodal-test2-vector.xml", fList, ";", Inf)                // version 2.0 file (no standard yet)
699        fList = AddListItem("test.xml",                                 fList, ";", Inf)                // cs_collagen.xml with no namespace
700        fList = AddListItem("test2.xml",                                fList, ";", Inf)                // version 2.0 file (no standard yet)
701        fList = AddListItem("ISIS_SANS_Example.xml",    fList, ";", Inf)                // from S. King, 2008-03-17
702        fList = AddListItem("W1W2.xml",                                 fList, ";", Inf)                // from S. King, 2008-03-17
703        fList = AddListItem("ill_sasxml_example.xml",   fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
704        fList = AddListItem("isis_sasxml_example.xml",  fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
705        fList = AddListItem("r586.xml",                                         fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
706        fList = AddListItem("r597.xml",                                         fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
707        fList = AddListItem("xg009036_001.xml",                 fList, ";", Inf)                // foreign elements with other namespaces
708        fList = AddListItem("cs_collagen.xml",                  fList, ";", Inf)                // another simple dataset, bare minimum info
709        fList = AddListItem("cs_collagen_full.xml",             fList, ";", Inf)                // more Q range than previous
710        fList = AddListItem("cs_af1410.xml",                    fList, ";", Inf)                // multiple SASentry and SASdata elements
711        fList = AddListItem("cansas1d-template.xml",    fList, ";", Inf)                // multiple SASentry and SASdata elements
712        fList = AddListItem("1998spheres.xml",                  fList, ";", Inf)                // 2 SASentry, few thousand data points each
713        fList = AddListItem("does-not-exist-file.xml",          fList, ";", Inf)                // non-existent file
714        fList = AddListItem("cs_rr_polymers.xml",               fList, ";", Inf)                // Round Robin polymer samples from John Barnes @ NIST
715        fList = AddListItem("s81-polyurea.xml",                         fList, ";", Inf)                // polyurea from APS/USAXS/Indra (with extra metadata)
716       
717        // try to load each data set in the table
718        FOR ( i = 0; i < ItemsInList(fList) ; i += 1 )
719                theFile = StringFromList(i, fList)                                      // walk through all test files
720                // PRINT "file: ", theFile
721                pathInfo home
722                //IF (CS_XmlReader(theFile) == 0)                                       // did the XML reader return without an error code?
723                timerID = StartMStimer
724                result = CS_XmlReader(ParseFilePath(5,S_path,"*",0,0) + theFile)
725                seconds = StopMSTimer(timerID) * 1.0e-6
726                PRINT "\t Completed in ", seconds, " seconds"
727                IF (result == 0)    // did the XML reader return without an error code?
728                        prj_grabMyXmlData()                                             // move the data to my directory
729                ENDIF
730        ENDFOR
731END
732
733
734FUNCTION testCollette()
735                                        // !!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
736                                        //          THIS IS JUST AN EXAMPLE
737
738// suggestions from ISIS users
739        // 3.   Loading actual data from LOQ caused some problems.
740        //      Data created by Colette names files with run number.
741        //      When entering full path to load the data if you use "…\example\31531.X" Igor will read \3 as a character.
742        //      A simple fix which has worked for this is to use / instead of \ e.g. "…\example/31531.X".
743       
744        //4.    Once data is loaded in Igor it is relatively easy to work with but would be nicer if the SASdata
745        //      was loaded into root directory (named using run number rather than generically as it is at the moment) rather than another folder.
746        //This becomes more problematic when two samples are being loaded for comparison.
747        //      Although still relatively easy to work with, changing the folders can lead to mistakes being made.
748
749        //Say, for Run=31531, then Qsas_31531
750
751        CS_XmlReader("W1W2.XML")
752        STRING srcDir = "root:Packages:CS_XMLreader"
753        STRING destDir = "root", importFolder, target
754        Variable i, j
755        FOR ( i = 0; i < CountObjects(srcDir, 4) ; i += 1 )
756                SetDataFolder $srcDir
757                importFolder = GetIndexedObjName(srcDir, 4, i)
758                SetDataFolder $importFolder
759                IF ( EXISTS( "metadata" ) == 1 )
760                        // looks like a SAS data folder
761                        WAVE/T metadata
762                        STRING Run = ""
763                        FOR (j = 0; j < DimSize(metadata, 0); j += 1)
764                                IF ( CmpStr( "Run", metadata[j][0]) == 0 )
765                                        // get the Run number and "clean" it up a bit
766                                        Run = TrimWS(  ReplaceString("\\", metadata[j][1], "/")  )
767                                        // !!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
768                                        // need to find unique name for destination waves       
769                                        //          THIS IS JUST AN EXAMPLE
770                                        // Persons who implement this properly should be more elegant
771                                        // For now, I will blast any existing and proceed blindly.
772                                        target = "root:Qsas_" + Run
773                                        Duplicate/O Qsas, $target
774                                        target = "root:Isas_" + Run
775                                        Duplicate/O Isas, $target
776                                        IF ( exists( "Idev" ) == 1 )
777                                                target = "root:Idev_" + Run
778                                                Duplicate/O Idev, $target
779                                        ENDIF
780                                        IF ( exists( "Qdev" ) == 1 )
781                                                target = "root:Qdev_" + Run
782                                                Duplicate/O Qdev, $target
783                                        ENDIF
784                                        IF ( exists( "dQw" ) == 1 )
785                                                target = "root:QdQw_" + Run
786                                                Duplicate/O dQw, $target
787                                        ENDIF
788                                        IF ( exists( "dQl" ) == 1 )
789                                                target = "root:dQl_" + Run
790                                                Duplicate/O dQl, $target
791                                        ENDIF
792                                        IF ( exists( "Qmean" ) == 1 )
793                                                target = "root:Qmean_" + Run
794                                                Duplicate/O Qmean, $target
795                                        ENDIF
796                                        IF ( exists( "Shadowfactor" ) == 1 )
797                                                target = "root:Shadowfactor_" + Run
798                                                Duplicate/O Shadowfactor, $target
799                                        ENDIF
800                                        target = "root:metadata_" + Run
801                                        Duplicate/O/T metadata, $target
802                                        BREAK
803                                ENDIF
804                        ENDFOR
805                ENDIF
806        ENDFOR
807
808        SetDataFolder root:
809END
810
811#else   // if( Exists("XmlOpenFile") )
812        // No XMLutils XOP: provide dummy function so that IgorPro can compile dependent support code
813        FUNCTION CS_XmlReader(fileName)
814            String fileName
815            Abort  "XML function provided by XMLutils XOP is not available, get the XOP from : http://www.igorexchange.com/project/XMLutils (see http://www.smallangles.net/wgwiki/index.php/cansas1d_binding_IgorPro for details)"
816            RETURN(-6)
817        END
818#endif  // if( Exists("XmlOpenFile") )
Note: See TracBrowser for help on using the repository browser.