source: sasview/src/sans/dataloader/readers/cansas_reader.py @ eda8972

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since eda8972 was eda8972, checked in by Jeff Krzywon <jeffery.krzywon@…>, 11 years ago

I will finalize this on Monday.

  • Property mode set to 100644
File size: 30.8 KB
Line 
1"""
2    CanSAS data reader - new recursive cansasVersion.
3"""
4############################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#If you use DANSE applications to do scientific research that leads to
9#publication, we ask that you acknowledge the use of the software with the
10#following sentence:
11#This work benefited from DANSE software developed under NSF award DMR-0520547.
12#copyright 2008,2009 University of Tennessee
13#############################################################################
14
15import logging
16import numpy
17import os
18import sys
19from sans.dataloader.data_info import Data1D
20from sans.dataloader.data_info import Collimation
21from sans.dataloader.data_info import Detector
22from sans.dataloader.data_info import Process
23from sans.dataloader.data_info import Aperture
24import xml_reader
25import xml.dom.minidom
26from cansas_constants import cansasConstants
27
28_ZERO = 1e-16
29HAS_CONVERTER = True
30try:
31    from sans.data_util.nxsunit import Converter
32except:
33    HAS_CONVERTER = False
34   
35CANSAS_FORMAT = cansasConstants.CANSAS_FORMAT
36CANSAS_NS = cansasConstants.CANSAS_NS
37ALLOW_ALL = True
38
39
40def write_node(doc, parent, name, value, attr={}):
41    """
42    :param doc: document DOM
43    :param parent: parent node
44    :param name: tag of the element
45    :param value: value of the child text node
46    :param attr: attribute dictionary
47   
48    :return: True if something was appended, otherwise False
49    """
50    if value is not None:
51        node = doc.createElement(name)
52        node.appendChild(doc.createTextNode(str(value)))
53        for item in attr:
54            node.setAttribute(item, attr[item])
55        parent.appendChild(node)
56        return True
57    return False
58               
59
60def get_content(location, node):
61    """
62    Get the first instance of the content of a xpath location.
63   
64    :param location: xpath location
65    :param node: node to start at
66   
67    :return: Element, or None
68    """
69    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
70   
71    if len(nodes) > 0:
72        return nodes[0]
73    else:
74        return None
75
76
77def get_float(location, node):
78    """
79    Get the content of a node as a float
80   
81    :param location: xpath location
82    :param node: node to start at
83    """
84    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
85   
86    value = None
87    attr = {}
88    if len(nodes) > 0:
89        try:
90            value = float(nodes[0].text)
91        except:
92            # Could not pass, skip and return None
93            msg = "cansas_reader.get_float: could not "
94            msg += " convert '%s' to float" % nodes[0].text
95            logging.error(msg)
96        if nodes[0].get('unit') is not None:
97            attr['unit'] = nodes[0].get('unit')
98    return value, attr
99
100
101
102class CANSASError(Exception):
103    """Base class all CANSAS reader exceptions are derived"""
104    pass
105
106class NotCANSASFileError(CANSASError):
107    def __init__(self):
108        self.value = "This is not a proper CanSAS file."
109    def __str__(self):
110        return repr(self.value)
111
112class Reader():
113    """
114    Class to load cansas 1D XML files
115   
116    :Dependencies:
117        The CanSAS reader requires PyXML 0.8.4 or later.
118    """
119    ##CanSAS version - defaults to version 1.0
120    cansasVersion = "1.0"
121    ##Data reader
122    reader = xml_reader.XMLreader()
123    errors = []
124   
125    def __init__(self):
126        ## List of errors
127        self.errors = []
128       
129    def isCansas(self):
130        """
131        Checks to see if the xml file is a CanSAS file
132        """
133        if self.reader.validateXML():
134            xmlns = self.reader.xmlroot.keys()
135            if (CANSAS_NS.get(self.cansasVersion).get("ns") == self.reader.xmlroot.get(xmlns[1]).rsplit(" ")[0]):
136                return True
137        return False
138   
139    def read(self, xml):
140        """
141        Validate and read in an xml file in the canSAS format.
142       
143        :param xml: A canSAS file path in proper XML format
144        """
145        # X - Q value; Y - Intensity (Abs)
146        x = numpy.empty(0)
147        y = numpy.empty(0)
148        dx = numpy.empty(0)
149        dy = numpy.empty(0)
150        dxl = numpy.empty(0)
151        dxw = numpy.empty(0)
152       
153        # output - Final list of Data1D objects
154        output = []
155        # ns - Namespace hierarchy for current xml object
156        ns = []
157       
158        try:
159            # Load in the xml file and get the cansas version from the header
160            self.reader.setXMLFile(xml)
161            root = self.reader.xmlroot
162            self.cansasVersion = root.get("version")
163            # Generic values for the cansas file based on the version
164            cansas_defaults = CANSAS_NS.get(self.cansasVersion)
165       
166            # Link a schema to the XML file.
167            basename = os.path.basename(xml)
168            base_name = xml_reader.__file__
169            base = base_name.split("\\sans\\")[0]
170            schema_path = "{0}\\sans\\dataloader\\readers\\schema\\{1}".format(base, cansas_defaults.get("schema")).replace("\\", "/")
171            self.reader.setSchema(schema_path)
172       
173            # Try to load the file, but raise an error if unable to.
174            # Check the file matches the XML schema
175            if self.isCansas():
176                # Get each SASentry from the XML file and add it to a list.
177                entry_list = root.xpath('/ns:SASroot/ns:SASentry',
178                                             namespaces={'ns': cansas_defaults.get("ns")})
179                ns.append("SASentry")
180               
181                # If there are multiple files, modify the name for each is unique
182                multipleFiles = len(entry_list) - 1
183                n = 0
184                name = basename
185                # Parse each SASentry item
186                for entry in entry_list:
187                   
188                    # Define a new Data1D object with zeroes for x and y
189                    data1D = Data1D(x,y,dx,dy)
190                    data1D.dxl = dxl
191                    data1D.dxw = dxw
192                   
193                    # If more than one SASentry, number each in order
194                    if multipleFiles:
195                        name += "_{0}".format(n)
196                        n += 1
197                   
198                    # Set the Data1D name and then parse the entry. The entry is appended to a list of entry values
199                    data1D.filename = name
200                    data1D.meta_data["loader"] = "CanSAS 1D"
201                    return_value, extras = self._parse_entry(entry, ns, data1D)
202                    del extras[:]
203                   
204                    #Final cleanup - Remove empty nodes, verify array sizes are correct
205                    return_value.errors = self.errors
206                    del self.errors[:]
207                    numpy.trim_zeros(return_value.x)
208                    numpy.trim_zeros(return_value.y)
209                    numpy.trim_zeros(return_value.dy)
210                    size_dx = return_value.dx.size
211                    size_dxl = return_value.dxl.size
212                    size_dxw = return_value.dxw.size
213                    if size_dxl == 0 and size_dxw == 0:
214                        return_value.dxl = None
215                        return_value.dxw = None
216                        numpy.trim_zeros(return_value.dx)
217                    elif size_dx == 0:
218                        return_value.dx = None
219                        size_dx = size_dxl
220                        numpy.trim_zeros(return_value.dxl)
221                        numpy.trim_zeros(return_value.dxw)
222                   
223                    output.append(return_value)
224            else:
225                # If the file does not match the schema, raise this error
226                raise RuntimeError, "%s cannot be read \n" % xml
227        # If an exception occurs while loading the file, give a descriptive output.
228        except Exception:
229            raise RuntimeError, "%s cannot be read \n" % xml
230           
231        # Return a list of parsed entries that dataloader can manage
232        return output
233   
234    def _create_unique_key(self, dictionary, name, i):
235        if dictionary.get(name) is not None:
236            i += 1
237            name = name.split("_")[0]
238            name += "_{0}".format(i)
239            name = self._create_unique_key(dictionary, name, i)
240        return name
241   
242    def _iterate_namespace(self, ns):
243        # The current level to look through in cansas_constants.
244        current_level = CANSAS_FORMAT.get("SASentry")
245        # Defaults for variable and datatype
246        ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\""
247        ns_datatype = "content"
248        ns_optional = True
249        for name in ns:
250            if name != "SASentry":
251                current_level = current_level.get("children").get(name, "")
252                if current_level == "":
253                    current_level = current_level.get("<any>", "")
254                cl_variable = current_level.get("variable", "")
255                cl_datatype = current_level.get("storeas", "")
256                cl_units_optional = current_level.get("units_required", "")
257                # Where are how to store the variable for the given namespace
258                # The CANSAS_CONSTANTS tree is hierarchical, so is no value, inherit
259                ns_variable = cl_variable if cl_variable != "" else ns_variable
260                ns_datatype = cl_datatype if cl_datatype != "" else ns_datatype
261                ns_optional = cl_units_optional if cl_units_optional != ns_optional else ns_optional
262        return current_level, ns_variable, ns_datatype, ns_optional
263   
264    def _unit_conversion(self, new_current_level, attr, data1D, node_value, optional = True):
265        value_unit = ''
266        if 'unit' in attr and 'unit' in new_current_level:
267            try:
268                if isinstance(node_value, float) is False:
269                    exec("node_value = float({0})".format(node_value))
270                default_unit = None
271                unitname = new_current_level.get("unit")
272                exec "default_unit = data1D.{0}.lower()".format(unitname)
273                local_unit = attr['unit'].lower()
274                if local_unit != default_unit:
275                    if HAS_CONVERTER == True:
276                        try:
277                            data_conv_q = Converter(attr['unit'])
278                            value_unit = default_unit
279                            exec "node_value = data_conv_q(node_value, units=data1D.{0})".format(unitname)
280                        except:
281                            msg = "CanSAS reader: could not convert "
282                            msg += "Q unit [%s]; " % attr['unit'],
283                            exec "msg += \"expecting [%s]\n  %s\" % (data1D.{0}, sys.exc_info()[1])".format(unitname)
284                            raise ValueError, msg
285                    else:
286                        value_unit = local_unit
287                        err_mess = "CanSAS reader: unrecognized %s unit [%s];"\
288                        % (node_value, default_unit)
289                        err_mess += " expecting [%s]" % local_unit
290                        self.errors.append(err_mess)
291                        if optional:
292                            logging.info(err_mess)
293                        else:
294                            raise ValueError, err_mess
295            except Exception as e:
296                msg = "CanSAS reader: could not convert "
297                msg += "Q unit [%s]; " % attr['unit'],
298                exec "msg += \"expecting [%s]\n  %s\" % (data1D.{0}, sys.exc_info()[1])".format(unitname)
299                self.errors.append(msg)
300                raise ValueError, msg
301        elif 'unit' in attr:
302            value_unit = attr['unit']
303        node_value = "float({0})".format(node_value)
304        return node_value, value_unit
305   
306    def _parse_entry(self, dom, ns, data1D, extras = []):
307        """
308        Parse a SASEntry - new recursive method for parsing the dom of
309            the CanSAS data format. This will allow multiple data files
310            and extra nodes to be read in simultaneously.
311       
312        :param dom: dom object with a namespace base of ns
313        :param ns: A list of element names that lead up to the dom object
314        :param data1D: The data1D object that will be modified
315        """
316         
317        # A portion of every namespace entry
318        base_ns = "{0}{1}{2}".format("{", CANSAS_NS.get(self.cansasVersion).get("ns"), "}")
319        unit = ''
320       
321        # Go through each child in the parent element
322        for node in dom:
323            try:
324                # Get the element name and set the current ns level
325                tagname = node.tag.replace(base_ns, "")
326                tagname_original = tagname
327                ns.append(tagname)
328                attr = node.attrib
329               
330                # Look for special cases
331                save_data1D = data1D
332                if tagname == "SASdetector":
333                    data1D = Detector()
334                elif tagname == "SAScollimation":
335                    data1D = Collimation()
336                elif tagname == "SASprocess":
337                    data1D = Process()
338                    for child in node:
339                        if child.tag.replace(base_ns, "") == "term":
340                            term_attr = {}
341                            for attr in child.keys():
342                                term_attr[attr] = child.get(attr).strip()
343                            if child.text is not None:
344                                term_attr['value'] = child.text.strip()
345                                data1D.term.append(term_attr)
346                elif tagname == "aperture":
347                    data1D = Aperture()
348               
349                # Get where to store content
350                new_current_level, ns_variable, ns_datatype, optional = self._iterate_namespace(ns)
351                # If the element is a child element, recurse
352                if node.getchildren() is not None:
353                    # Returned value is new Data1D object with all previous and new values in it.
354                    data1D, extras = self._parse_entry(node, ns, data1D, extras)
355                   
356                #Get the information from the node
357                node_value = node.text
358                if node_value is not None:
359                    node_value = node_value.strip().replace("\r"," ").replace("\n"," ")
360                if node_value == "":
361                    node_value = None
362               
363                # If the value is a float, compile with units.
364                if ns_datatype == "float":
365                    # If an empty value is given, store as zero.
366                    if node_value is None:
367                        node_value = "0.0"
368                    elif node_value.isspace():
369                        node_value = "0.0"
370                    node_value, unit = self._unit_conversion(new_current_level, attr, data1D, node_value, optional)
371                   
372                # If appending to a dictionary (meta_data | run_name), name sure the key is unique
373                if ns_variable == "{0}.meta_data[\"{2}\"] = \"{1}\"":
374                    # If we are within a Process, Detector, Collimation or Aperture instance, pull out old data1D
375                    tagname = self._create_unique_key(data1D.meta_data, tagname, 0)
376                    if isinstance(data1D, Data1D) == False:
377                        store_me = ns_variable.format("data1D", node_value, tagname)
378                        extras.append(store_me)
379                        ns_variable = None
380                if ns_variable == "{0}.run_name[\"{2}\"] = \"{1}\"":
381                    tagname = self._create_unique_key(data1D.run_name, tagname, 0)
382               
383                # Check for Data1D object and any extra commands to save
384                if isinstance(data1D, Data1D):
385                    for item in extras:
386                        exec item
387                # Don't bother saving empty information unless it is a float
388                if ns_variable is not None and node_value is not None and node_value.isspace() == False:
389                    # Format a string and then execute it.
390                    store_me = ns_variable.format("data1D", node_value, tagname)
391                    exec store_me
392                # Get attributes and process them
393                if attr is not None:
394                    for key in node.keys():
395                        try:
396                            cansas_attrib = new_current_level.get("attributes").get(key)
397                            attrib_variable = cansas_attrib.get("variable")
398                            if key == 'unit' and unit != '':
399                                attrib_value = unit
400                            else:
401                                attrib_value = node.attrib[key]
402                            store_attr = attrib_variable.format("data1D", attrib_value, key)
403                            exec store_attr
404                        except AttributeError as e:
405                            pass
406                           
407                     
408            except Exception as e:
409                exc_type, exc_obj, exc_tb = sys.exc_info()
410                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
411                print(e, exc_type, fname, exc_tb.tb_lineno, tagname, exc_obj)
412            finally:
413                # Save special cases in original data1D object and then restore the data1D
414                if tagname_original == "SASdetector":
415                    save_data1D.detector.append(data1D)
416                elif tagname_original == "SAScollimation":
417                    save_data1D.collimation.append(data1D)
418                elif tagname_original == "SASprocess":
419                    save_data1D.process.append(data1D)
420                elif tagname_original == "aperture":
421                    save_data1D.aperture.append(data1D)
422                else:
423                    save_data1D = data1D
424                data1D = save_data1D
425                # Remove tagname from ns to restore original base
426                ns.remove(tagname_original)
427       
428        return data1D, extras
429       
430    def _to_xml_doc(self, datainfo):
431        """
432        Create an XML document to contain the content of a Data1D
433       
434        :param datainfo: Data1D object
435        """
436       
437        if not issubclass(datainfo.__class__, Data1D):
438            raise RuntimeError, "The cansas writer expects a Data1D instance"
439       
440        doc = xml.dom.minidom.Document()
441        main_node = doc.createElement("SASroot")
442        main_node.setAttribute("cansasVersion", self.cansasVersion)
443        main_node.setAttribute("xmlns", "cansas1d/%s" % self.cansasVersion)
444        main_node.setAttribute("xmlns:xsi",
445                               "http://www.w3.org/2001/XMLSchema-instance")
446        main_node.setAttribute("xsi:schemaLocation",
447                               "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.cansasVersion)
448       
449        doc.appendChild(main_node)
450       
451        entry_node = doc.createElement("SASentry")
452        main_node.appendChild(entry_node)
453       
454        write_node(doc, entry_node, "Title", datainfo.title)
455        for item in datainfo.run:
456            runname = {}
457            if item in datainfo.run_name and \
458            len(str(datainfo.run_name[item])) > 1:
459                runname = {'name': datainfo.run_name[item]}
460            write_node(doc, entry_node, "Run", item, runname)
461       
462        # Data info
463        node = doc.createElement("SASdata")
464        entry_node.appendChild(node)
465       
466        for i in range(len(datainfo.x)):
467            pt = doc.createElement("Idata")
468            node.appendChild(pt)
469            write_node(doc, pt, "Q", datainfo.x[i], {'unit': datainfo.x_unit})
470            if len(datainfo.y) >= i:
471                write_node(doc, pt, "I", datainfo.y[i],
472                            {'unit': datainfo.y_unit})
473            if datainfo.dx != None and len(datainfo.dx) >= i:
474                write_node(doc, pt, "Qdev", datainfo.dx[i],
475                            {'unit': datainfo.x_unit})
476            if datainfo.dxl != None and len(datainfo.dxl) >= i:
477                write_node(doc, pt, "dQl", datainfo.dxl[i],
478                            {'unit': datainfo.x_unit})
479            if datainfo.dxw != None and len(datainfo.dxw) >= i:
480                write_node(doc, pt, "dQw", datainfo.dxw[i],
481                            {'unit': datainfo.x_unit})
482            if datainfo.dy != None and len(datainfo.dy) >= i:
483                write_node(doc, pt, "Idev", datainfo.dy[i],
484                            {'unit': datainfo.y_unit})
485
486        # Sample info
487        sample = doc.createElement("SASsample")
488        if datainfo.sample.name is not None:
489            sample.setAttribute("name", str(datainfo.sample.name))
490        entry_node.appendChild(sample)
491        write_node(doc, sample, "ID", str(datainfo.sample.ID))
492        write_node(doc, sample, "thickness", datainfo.sample.thickness,
493                   {"unit": datainfo.sample.thickness_unit})
494        write_node(doc, sample, "transmission", datainfo.sample.transmission)
495        write_node(doc, sample, "temperature", datainfo.sample.temperature,
496                   {"unit": datainfo.sample.temperature_unit})
497       
498        for item in datainfo.sample.details:
499            write_node(doc, sample, "details", item)
500       
501        pos = doc.createElement("position")
502        written = write_node(doc, pos, "x", datainfo.sample.position.x,
503                             {"unit": datainfo.sample.position_unit})
504        written = written | write_node(doc, pos, "y",
505                                       datainfo.sample.position.y,
506                                       {"unit": datainfo.sample.position_unit})
507        written = written | write_node(doc, pos, "z",
508                                       datainfo.sample.position.z,
509                                       {"unit": datainfo.sample.position_unit})
510        if written == True:
511            sample.appendChild(pos)
512       
513        ori = doc.createElement("orientation")
514        written = write_node(doc, ori, "roll",
515                             datainfo.sample.orientation.x,
516                             {"unit": datainfo.sample.orientation_unit})
517        written = written | write_node(doc, ori, "pitch",
518                                       datainfo.sample.orientation.y,
519                                    {"unit": datainfo.sample.orientation_unit})
520        written = written | write_node(doc, ori, "yaw",
521                                       datainfo.sample.orientation.z,
522                                    {"unit": datainfo.sample.orientation_unit})
523        if written == True:
524            sample.appendChild(ori)
525       
526        # Instrument info
527        instr = doc.createElement("SASinstrument")
528        entry_node.appendChild(instr)
529       
530        write_node(doc, instr, "name", datainfo.instrument)
531       
532        #   Source
533        source = doc.createElement("SASsource")
534        if datainfo.source.name is not None:
535            source.setAttribute("name", str(datainfo.source.name))
536        instr.appendChild(source)
537       
538        write_node(doc, source, "radiation", datainfo.source.radiation)
539        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
540        size = doc.createElement("beam_size")
541        if datainfo.source.beam_size_name is not None:
542            size.setAttribute("name", str(datainfo.source.beam_size_name))
543        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
544                             {"unit": datainfo.source.beam_size_unit})
545        written = written | write_node(doc, size, "y",
546                                       datainfo.source.beam_size.y,
547                                       {"unit": datainfo.source.beam_size_unit})
548        written = written | write_node(doc, size, "z",
549                                       datainfo.source.beam_size.z,
550                                       {"unit": datainfo.source.beam_size_unit})
551        if written == True:
552            source.appendChild(size)
553           
554        write_node(doc, source, "wavelength",
555                   datainfo.source.wavelength,
556                   {"unit": datainfo.source.wavelength_unit})
557        write_node(doc, source, "wavelength_min",
558                   datainfo.source.wavelength_min,
559                   {"unit": datainfo.source.wavelength_min_unit})
560        write_node(doc, source, "wavelength_max",
561                   datainfo.source.wavelength_max,
562                   {"unit": datainfo.source.wavelength_max_unit})
563        write_node(doc, source, "wavelength_spread",
564                   datainfo.source.wavelength_spread,
565                   {"unit": datainfo.source.wavelength_spread_unit})
566       
567        #   Collimation
568        for item in datainfo.collimation:
569            coll = doc.createElement("SAScollimation")
570            if item.name is not None:
571                coll.setAttribute("name", str(item.name))
572            instr.appendChild(coll)
573           
574            write_node(doc, coll, "length", item.length,
575                       {"unit": item.length_unit})
576           
577            for apert in item.aperture:
578                ap = doc.createElement("aperture")
579                if apert.name is not None:
580                    ap.setAttribute("name", str(apert.name))
581                if apert.type is not None:
582                    ap.setAttribute("type", str(apert.type))
583                coll.appendChild(ap)
584               
585                write_node(doc, ap, "distance", apert.distance,
586                           {"unit": apert.distance_unit})
587               
588                size = doc.createElement("size")
589                if apert.size_name is not None:
590                    size.setAttribute("name", str(apert.size_name))
591                written = write_node(doc, size, "x", apert.size.x,
592                                     {"unit": apert.size_unit})
593                written = written | write_node(doc, size, "y", apert.size.y,
594                                               {"unit": apert.size_unit})
595                written = written | write_node(doc, size, "z", apert.size.z,
596                                               {"unit": apert.size_unit})
597                if written == True:
598                    ap.appendChild(size)
599
600        #   Detectors
601        for item in datainfo.detector:
602            det = doc.createElement("SASdetector")
603            written = write_node(doc, det, "name", item.name)
604            written = written | write_node(doc, det, "SDD", item.distance,
605                                           {"unit": item.distance_unit})
606            written = written | write_node(doc, det, "slit_length",
607                                           item.slit_length,
608                                           {"unit": item.slit_length_unit})
609            if written == True:
610                instr.appendChild(det)
611           
612            off = doc.createElement("offset")
613            written = write_node(doc, off, "x", item.offset.x,
614                                 {"unit": item.offset_unit})
615            written = written | write_node(doc, off, "y", item.offset.y,
616                                           {"unit": item.offset_unit})
617            written = written | write_node(doc, off, "z", item.offset.z,
618                                           {"unit": item.offset_unit})
619            if written == True:
620                det.appendChild(off)
621           
622            center = doc.createElement("beam_center")
623            written = write_node(doc, center, "x", item.beam_center.x,
624                                 {"unit": item.beam_center_unit})
625            written = written | write_node(doc, center, "y",
626                                           item.beam_center.y,
627                                           {"unit": item.beam_center_unit})
628            written = written | write_node(doc, center, "z",
629                                           item.beam_center.z,
630                                           {"unit": item.beam_center_unit})
631            if written == True:
632                det.appendChild(center)
633               
634            pix = doc.createElement("pixel_size")
635            written = write_node(doc, pix, "x", item.pixel_size.x,
636                                 {"unit": item.pixel_size_unit})
637            written = written | write_node(doc, pix, "y", item.pixel_size.y,
638                                           {"unit": item.pixel_size_unit})
639            written = written | write_node(doc, pix, "z", item.pixel_size.z,
640                                           {"unit": item.pixel_size_unit})
641            if written == True:
642                det.appendChild(pix)
643               
644            ori = doc.createElement("orientation")
645            written = write_node(doc, ori, "roll", item.orientation.x,
646                                 {"unit": item.orientation_unit})
647            written = written | write_node(doc, ori, "pitch",
648                                           item.orientation.y,
649                                           {"unit": item.orientation_unit})
650            written = written | write_node(doc, ori, "yaw",
651                                           item.orientation.z,
652                                           {"unit": item.orientation_unit})
653            if written == True:
654                det.appendChild(ori)
655               
656        # Processes info
657        for item in datainfo.process:
658            node = doc.createElement("SASprocess")
659            entry_node.appendChild(node)
660
661            write_node(doc, node, "name", item.name)
662            write_node(doc, node, "date", item.date)
663            write_node(doc, node, "description", item.description)
664            for term in item.term:
665                value = term['value']
666                del term['value']
667                write_node(doc, node, "term", value, term)
668            for note in item.notes:
669                write_node(doc, node, "SASprocessnote", note)
670       
671        # Return the document, and the SASentry node associated with
672        # the data we just wrote
673        return doc, entry_node
674           
675    def write(self, filename, datainfo):
676        """
677        Write the content of a Data1D as a CanSAS XML file
678       
679        :param filename: name of the file to write
680        :param datainfo: Data1D object
681        """
682        # Create XML document
683        doc, _ = self._to_xml_doc(datainfo)
684        # Write the file
685        fd = open(filename, 'w')
686        fd.write(doc.toprettyxml())
687        fd.close()
Note: See TracBrowser for help on using the repository browser.