source: SHX/trunk/SeismicHandler/modules/traces.py @ 1130

Revision 1130, 12.7 KB checked in by klaus, 4 years ago (diff)

bugfix in metadata update; bugfix in menue entries using fdsn reading

  • Property svn:eol-style set to native
Line 
1# -*- coding: utf-8 -*-
2
3#    This file is part of Seismic Handler eXtended (SHX). For terms of use and
4#    license information please see license.txt and visit
5#    http://www.seismic-handler.org/portal/wiki/Shx/LicenseTerms
6
7import numpy as np
8from obspy.core.util import AttribDict
9from SeismicHandler.basics import Singleton, timeit, timestore
10from SeismicHandler.basics.messages import log_message, subscribe_event
11# disabled: _set_runtime_var
12from obspy.core.stream import Stream, Trace
13from SeismicHandler.config import Settings, set_dsptrcs, set_tottrcs
14from SeismicHandler.modules.stations import Stations
15
16
17META_STATUS_EMPTY    = 0
18META_STATUS_LOCATION = 1
19META_STATUS_COMPLETE = 2
20
21
22class Traces(object):
23    """
24    Supply access for two streams of traces. One is used for plotting, the other
25    holds hidden traces. This will be enhanced later, but for compatibility
26    reasons to existing command procedures it's done that way first.
27    """
28    __metaclass__ = Singleton
29
30    traces = AttribDict()
31
32    def __init__(self, *args, **kwargs):
33        self.traces.visible = Stream()
34        self.traces.hidden = Stream()
35        self.traces.created = Stream()
36        self.traces.overlays = OverlayTraces()
37        self.netdict = {}
38
39    def __getattr__(self, name):
40        try:
41            return self.__class__.__dict__["traces"][name]
42        except:
43            raise AttributeError(name)
44
45    #@timeit
46    def addreplace(self, stream, applyGain=False):
47        """
48        Adds or replaces visible streams depending on runtime setting.
49        """
50        # this is always a global switch!
51        if not Settings.swKeeptraces:
52            log_message("debug.traces", "replacing visible traces")
53            self.traces.visible.clear()
54
55        # check if traces contains relevant information
56        # single trace
57        if isinstance(stream, Trace):
58            stream = Stream(traces=[stream])
59        if isinstance(stream, list):
60            stream = Stream(traces=stream)
61
62        for t in stream:
63            t.establish()
64            t.invalidate_cache()
65            self.cacheNetcode(t)
66
67            if applyGain:
68                # get gain from inventory and apply it to data
69                gain = None
70                try:
71                    gain = float(Stations()[t.id, t.stats.starttime].gain)
72                except KeyError:
73                    msg = "no meta data stored for station '%s' at %s"
74                    log_message("info.traces", msg % (t.id, t.get_info("start")))
75                # data from trace itself, this will override stored information
76                try:
77                    gain = 1e9/t.stats.paz.sensitivity
78                    msg = "gain information attached to trace: %f"
79                    log_message("info.traces", msg % gain)
80                except:
81                    pass
82
83                if gain is None:
84                    msg = "No gain configured for %s at %s"
85                    log_message("warning.traces", msg % (t.id, t.get_info("start")))
86                    gain = 1.
87
88                t.data *= gain
89                msg = "Applied gain factor of %f to %s"
90                log_message("debug.traces", msg % (gain, t.id))
91           
92            t.set_info( 'METASTATUS', self.getMetaStatus(t) )
93       
94        self.traces.visible += stream
95        self.traces.created.clear()
96        self.traces.created += stream
97        self.updateCounter()
98
99    #@timeit
100    def updateCounter(self):
101        dsptrcs = len(self.traces.visible)
102        hidtrcs = len(self.traces.hidden)
103        log_message("info.traces", "Now %u visible and %u hidden traces" % (
104                                                             dsptrcs, hidtrcs))
105
106        #_set_runtime_var("dsptrcs", dsptrcs)
107        #_set_runtime_var("tottrcs", dsptrcs+hidtrcs)
108        set_dsptrcs( dsptrcs )
109        set_tottrcs( dsptrcs+hidtrcs )
110
111    #@timeit
112    def visible_index(self, trace):
113        """
114        Returns position of trace inside visible traces list.
115        """
116        return self.traces.visible.traces.index(trace)
117   
118    def cacheNetcode( self, trace ):
119        """Store netcodes for known stations. Overwrites possibly existing
120        old entries.
121        """
122        upstat = trace.stats.station.upper()
123        self.netdict[upstat] = trace.stats.network.upper()
124   
125    def getNetcode( self, station ):
126        return self.netdict.get( station.upper(), None )
127   
128    def getMetaStatus( self, trace, addgain=False ):
129        "Returns 0, 1 or 2 depending on metadata status."
130        stations = Stations()
131        sname = "%s.%s.%s.%s" % (trace.stats.network, trace.stats.station,
132            trace.stats.location, trace.stats.channel)
133        metatime = trace.stats.starttime \
134            + (trace.stats.endtime-trace.stats.starttime)/2
135        try:
136            r = stations[(sname, metatime)]
137        except:
138            if addgain:
139                return (META_STATUS_EMPTY,None)
140            else:
141                return META_STATUS_EMPTY
142        try:
143            slat = float( r.latitude )
144            slon = float( r.longitude )
145        except:
146            if addgain:
147                return (META_STATUS_EMPTY,None)
148            else:
149                return META_STATUS_EMPTY
150        try:
151            gain = float( r.gain )
152        except:
153            if addgain:
154                return (META_STATUS_LOCATION,None)
155            else:
156                return META_STATUS_LOCATION
157        if gain == 1.0:
158            if addgain:
159                return (META_STATUS_LOCATION,None)
160            else:
161                return META_STATUS_LOCATION
162        if r.poles and r.poles != '[]':
163            if addgain:
164                return (META_STATUS_COMPLETE,gain)
165            else:
166                return META_STATUS_COMPLETE
167        else:
168            if addgain:
169                return (META_STATUS_LOCATION,gain)
170            else:
171                return META_STATUS_LOCATION
172       
173
174
175class OverlayTraces:
176    """
177    Store info about traces drawn above others.
178    Example:
179        overlaydict = {'A':[1,2],'B':[3,4]}
180        allidx = [1,2,3,4]
181    """
182   
183    def __init__( self ):
184        self.clearOverlays()
185       
186    def numberOfOverlays( self ):
187        return (len(self.allidx) - len(self.overlaydict))
188   
189    def addOverlay( self, idxlist ):
190        if len(idxlist) < 2:
191            print "should not happen: short overlay list"
192            return
193        # Check for already specified indices, no index may occur twice.
194        for i in idxlist:
195            if i in self.allidx:
196                print "trace '%s' already in overlay" % i
197                return
198        self.overlaydict[self.nextname] = idxlist
199        self.nextname = chr(ord(self.nextname)+1)
200        self.allidx += idxlist
201        #self.dump()
202   
203    def clearOverlays( self ):
204        self.overlaydict = {}
205        self.allidx = []
206        self.nextname = 'A'
207        self.initPlot(0,0)
208   
209    def initPlot( self, numtraces, traceorder ):
210        "Reset plot parameters before each redraw."
211        self.posdict = {}
212        self.posstore = []
213        self.posidx = -1
214        self.numtraces = numtraces
215        self.traceorder = traceorder
216   
217    def plotPos( self, trcidx, ypos ):
218        "Change plot positions of traces according to overlays."
219        if len(self.overlaydict.keys()) == 0:
220            return ypos
221        if self.traceorder == 0:
222            # traces always plotted top-down
223            trcidx = self.numtraces + 1 - trcidx
224        self.posstore.append( ypos )
225        for ovk in self.overlaydict.keys():
226            if trcidx in self.overlaydict[ovk]:
227                if ovk in self.posdict.keys():
228                    return self.posdict[ovk]
229                else:
230                    self.posidx += 1
231                    self.posdict[ovk] = self.posstore[self.posidx]
232                    return self.posstore[self.posidx]
233        self.posidx += 1
234        return self.posstore[self.posidx]
235   
236    def dump( self ):
237        print 'overlay dict', self.overlaydict
238        print 'overlay allidx', self.allidx
239        print 'overlay pos', self.posdict
240
241#@timeit
242def traces_from_list(selection):
243    """
244    Expand comma separated list of traces into Stream of internal traces.
245
246    Final Stream may contain visible and hidden traces!
247    """
248
249    if isinstance(selection, int):
250        selection = str(selection)
251
252    selection = selection.lower()
253    traces = Traces()
254
255    if selection.startswith("h:"):
256        parents = [traces.traces.hidden]
257        selection = selection[2:]
258    else:
259        parents = [traces.traces.visible]
260
261    selection = selection.split(",")
262
263    if "all" in selection:
264        return parents[0][:]
265
266    if selection[0].startswith("_"):
267        identifier = selection[0][1:].lower()
268
269        if identifier == "created":
270            return [t for t in traces.traces.created]
271
272        # pay attention to visible and invisible traces!
273        # this makes no sense in all cases but sometimes it does...
274        parents = [traces.traces.visible, traces.traces.hidden]
275
276        # get limitations
277        identifier = identifier.split("(")
278        if len(identifier) != 2:
279            return []
280
281        limits = identifier[1][:-1].split(":")
282        if len(limits) == 1:
283            limits.append(limits[0])
284
285        if identifier[0].endswith("~"):
286            ident = identifier[0][:-1]
287            negate = True
288        else:
289            ident = identifier[0]
290            negate = False
291
292        straces = []
293        for tl in parents:
294            for t in tl:
295                try:
296                    check = t.get_info(ident)
297                except:
298                    continue
299                if hasattr(check,'lower'):
300                    check = check.lower()
301               
302                found = False
303                try:
304                    if check >= type(check)(limits[0]) and \
305                                               check <= type(check)(limits[1]):
306                        found = True
307                except:
308                    continue
309
310                if found == negate:
311                    continue
312
313                straces.append(t)
314
315        return straces
316
317    numbers = expandTracelist(selection)
318
319    straces = []
320    for i in numbers:
321        try:
322            # get trace
323            straces.append(parents[0][i])
324        except IndexError:
325            log_message("warning", "trace number %s not found" % (i+1))
326
327    return Stream(straces)
328
329
330#@timeit
331def expandTracelist(selection):
332    """
333    Returns number of trace selection, e.g. 1,3-5 results in [0,2,3,4]. The
334    special value "all" must be treated outside this method.
335
336    >>> expandTracelist("1,3")
337    [0, 2]
338    >>> expandTracelist("3-5,3")
339    [2, 3, 4]
340    >>> expandTracelist(["1", "8-11"])
341    [0, 7, 8, 9, 10]
342    """
343    if isinstance(selection, basestring):
344        selection = selection.split(",")
345
346    numbers = []
347    for s in selection:
348        # range
349        if "-" in s:
350            try:
351                start, stop = map(int, s.split("-"))
352            except:
353                print "  skipping '%s'" % s
354                continue
355
356            if stop < start:
357                start, stop = stop, start
358
359            numbers.extend(range(start - 1, stop))
360        else:
361            if s.isdigit():
362                numbers.append(int(s) - 1)
363            else:
364                print "  skipping '%s'" % s
365
366    # unique
367    numbers = list(set(numbers))
368    numbers.sort()
369
370    return numbers
371
372
373#@timeit
374def new_starttime(src, dst, offset):
375    """
376    Set new starttime with respect to offset, trace's T-ORIGIN and source
377    trace.
378    """
379    dst.stats.starttime = src.stats.starttime
380
381    t_origin = dst.get_info("t-origin")
382    corr = offset - t_origin
383    if corr > 0:
384        dst.stats.starttime += corr
385
386
387#@timeit
388def new_trace_data(length, zero=False):
389    """
390    Return numpy array of length "length". If "zero" is True, all values
391    will be set to 0.
392    """
393
394    if zero:
395        return np.zeros(length, dtype="float32")
396    else:
397        return np.empty(length, dtype="float32")
398
399
400def add_traces(stream, gain=False):
401    """
402    Helper method for "Traces().addreplace(...)
403    """
404    Traces().addreplace(stream, applyGain=gain)
405
406def get_meta_status( trace, addgain=False ):
407    return Traces().getMetaStatus( trace, addgain=addgain )
408
409
410#@timeit
411def invalidate_all_traces(payload=None):
412    """
413    Voids all trace caches.
414    """
415    for t in Traces().visible:
416        t.invalidate_cache()
417subscribe_event(invalidate_all_traces, "recache")
418
419def addNetcode( station ):
420    "Adds netcode to station if not yet specified and found in cache."
421    traces = Traces()
422    upstat = station.upper()
423    if '.' in station:
424        return upstat
425    netcode = traces.getNetcode( upstat )
426    if netcode == None:
427        return upstat
428    return "%s.%s" % (netcode,upstat)
429
430
431if __name__ == "__main__":
432    import doctest
433    doctest.testmod(exclude_empty=True)
Note: See TracBrowser for help on using the repository browser.