source: SH_SHM/trunk/util/quakeml/parse.py @ 318

Revision 318, 6.1 KB checked in by marcus, 13 years ago (diff)

r157 | walther | 2011-02-10 12:46:56 +0100 (Do, 10 Feb 2011) | 4 lines

Subversion export from BGR/SZO/SZGRF internal used utilities at revision

  1. Since this software is only used in Seismic Handler context,

future enhancements will be done here ("the software got a new home").

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1#! /usr/bin/python
2# -*- coding: UTF8 -*-
3
4import sys
5try:
6    from lxml import etree
7except ImportError:
8    from xml.etree import ElementTree
9
10from decimal import Decimal
11
12class Event(object):
13    """
14    Seismic event.
15    """
16
17    def __str__(self):
18        return str(self.__dict__)
19
20class QuakeML(object):
21    """
22    Read access for QuakeML file-like objects.
23
24    >>> q = QuakeML(open("example.xml"), True)
25    >>> q.rawdata[40:92]
26    'quakeml xmlns="http://quakeml.org/xmlns/quakeml/1.0"'
27    >>> len(q.events)
28    9
29    >>> q.events[0].origin
30    '2010-07-07T04:03:57.7+00:00'
31    >>> q.events[3].description
32    'TAJIKISTAN'
33    >>> q.events[1].agencyOrigin
34    'smi:smi-registry/organization/NNC'
35    >>> q.events[2].agencyMagnitude
36    'smi:smi-registry/organization/GFZ'
37    """
38
39    namespace = {"qml": "http://quakeml.org/xmlns/quakeml/1.0"}
40
41    def __init__(self, stream, debug=False):
42        """
43        Read stream content (raw).
44        """
45
46        # There are two debug levels:
47        # "1": normal debugging
48        # "2": additionally write XML input data to "debug-dump.xml"
49        self.debugLevel = debug
50
51        try:
52            self.rawdata = stream.read()
53            self.debug("Data input length %u bytes." % len(self.rawdata))
54        except:
55            quit("input not readable.")
56
57        if self.debugLevel > 1:
58            try:
59                open("debug-dump.xml", "w").write(self.rawdata)
60                self.debug("Raw XML data written to 'debug-dump.xml'")
61            except Exception, e:
62                self.debug("Raw XML data dump requested, BUT an error "
63                           "occurred. %s" % e)
64
65        self.events = {}
66        self._initXML()
67        self._parseEvents()
68
69    def debug(self, text, nontext=None, spaces=0):
70        """
71        Simple debug function.
72        """
73
74        if not self.debugLevel:
75            return
76
77        print >> sys.stderr, text
78
79        if not nontext:
80            return
81
82        if type(nontext) == dict:
83            for i in nontext:
84                print >> sys.stderr, " "*spaces + i, nontext[i]
85
86    def _initXML(self):
87        """
88        Inititialize XML processor(s).
89       
90        Depending on available modules the method "xpathns" is set to the
91        corresponding helper function:
92
93        __lxml_xpathns - uses lxml.etree xpath method
94        __xml_findallns - uses xml.etree.ElementTree findall method
95
96        Both methods add the quakeml namespace to the path automatically.
97        """
98
99        if "etree" in globals().keys():
100            # use lxml
101            try:
102                parser = etree.XMLParser(remove_blank_text=True)
103                self.xml = etree.fromstring(self.rawdata, parser)
104                self.debug("LXML parser initiated.")
105                self.xpathns = self.__lxml_xpathns
106            except Exception, e:
107                quit("error while parsing XML: " + e)
108        else:
109            # use built-in xml.etree
110            try:
111                self.xml = ElementTree.XML(self.rawdata)
112                self.debug("Standard XML parser initiated.")
113                self.xpathns = self.__xml_findallns
114            except Exception, e:
115                quit("error while parsing XML: " + e)
116
117    def __lxml_xpathns(self, xml, path, select=0):
118        """
119        Helper function to handle global quakeml namespace for lxml.
120        """
121        # add namespace prefix
122        path = "/qml:".join(path.split("/"))
123       
124        if select == None:
125            return xml.xpath(path, namespaces=self.namespace)
126        else:
127            return xml.xpath(path, namespaces=self.namespace)[select]
128
129    def __xml_findallns(self, xml, path, select=0):
130        """
131        Helper function for namespace handling (standard xml module).
132        """
133        x = "/{%s}" % self.namespace.values()[0]
134        path = x.join(path.split("/"))
135
136        if select == None:
137            return xml.findall(path)
138        else:
139            return xml.findall(path)[select]
140   
141    def _parseEvents(self):
142        """
143        Loop through events inside XML data schema.
144        """
145
146        for event in self.xpathns(self.xml, "./eventParameters/event", select=None):
147            # mostly there is more than one origin
148            self.debug("Processing event %s" % event.attrib["publicID"])
149            origins = self.xpathns(event, "./origin", select=None)
150            for o in origins:
151                publicID = o.attrib["publicID"]
152                self.debug(" - origin %s" % publicID)
153
154                self.events[publicID] = e = Event()
155                # there's only a global description of source region
156                e.description = self.xpathns(event, "./description/text").text
157                # save publicID of associated event
158                e.eventID = event.attrib["publicID"]
159                e.publicID = publicID
160
161                # detailed information of single origins
162                e.origin = self.xpathns(o, "./time/value").text
163                e.longitude = Decimal(self.xpathns(o, "./longitude/value").text)
164                e.latitude = Decimal(self.xpathns(o, "./latitude/value").text)
165                e.depth = self.xpathns(o, "./depth/value").text
166                e.mode = self.xpathns(o, "./evaluationMode").text
167                e.agencyOrigin = self.xpathns(o, "./creationInfo/authorURI").text
168
169            # add associated magnitude to origin
170            magnitudes = self.xpathns(event, "./magnitude", select=None)
171            for m in magnitudes:
172                originID = self.xpathns(m, "./originID").text
173                e = self.events[originID]
174                e.magnitude = Decimal(self.xpathns(m, "./mag/value").text)
175                e.magnitudeType = self.xpathns(m, "./type").text
176                e.agencyMagnitude = self.xpathns(m, "./creationInfo/authorURI").text
177
178        # sort events by origin (results in a list)
179        self.events = self.events.values()
180        self.events.sort(key=lambda x: x.origin)
181
182        if not self.debugLevel:
183            return
184
185        for e in self.events:
186            self.debug("Origin details:", vars(e), 2)
187
188if __name__ == "__main__":
189    import doctest
190    errors, _ = doctest.testmod(exclude_empty=True)
Note: See TracBrowser for help on using the repository browser.