source: SHX/trunk/SeismicHandler/patches/ObsPy.py @ 434

Revision 434, 8.0 KB checked in by marcus, 12 years ago (diff)

Enforcing float32 data type for all traces (default in SH C backend). This will be changed in later versions.

  • Property svn:eol-style set to native
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2008-2011 Marcus Walther (walther@szgrf.bgr.de)
4#
5# This file is part of Seismic Handler eXtended (SHX)
6# Full details can be found at project website http://www.seismic-handler.org/
7#
8# SHX is free software; you can redistribute it and/or modify
9# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
10# the Free Software Foundation; either version 3 of the License, or
11# (at your option) any later version.
12#
13# SHX is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU Lesser General Public License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public License
19# along with SHX (see license.txt); if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
21
22"""
23This modules defines extensions to obspy. They are patched into the
24original obspy modules at a very basic level in SeismicHandler/__init__.py
25
26This module is named ObsPy.py to prevent import loops.
27"""
28
29import numpy as np
30import math
31from obspy.core import Stream, Trace
32from obspy.core.util import AttribDict
33from SeismicHandler.utils.pubsub import pub as msgs
34#from obspy.core import UTCDateTime
35
36__all__ = [
37    "patched",
38]
39
40def _streamAutoMerge(self, *args, **kwargs):
41    """
42    Call obspy's Stream merge method to fill up gaps using the latest pre-gap
43    sample value. This destroys the spectra of the data but it's common.
44    """
45
46    # Change value data type to float32 as in former versions of SH.
47    # This will be changed in later versions.
48    for t in self.traces:
49        if t.data.dtype != np.dtype('float32'):
50            t.data = t.data.astype('float32')
51
52    if self.traces:
53        self.merge(method=1, fill_value='latest')
54
55def streamAutoMergeInit(self, *args, **kwargs):
56    self.__shx_init__(*args, **kwargs)
57    _streamAutoMerge(self)
58
59def streamAutoMergeAppend(self, *args, **kwargs):
60    self.__shx_append(*args, **kwargs)
61    _streamAutoMerge(self, *args, **kwargs)
62
63def streamAutoMergeExtend(self, *args, **kwargs):
64    self.__shx_extend(*args, **kwargs)
65    _streamAutoMerge(self, *args, **kwargs)
66
67def streamAutoMergeInsert(self, *args, **kwargs):
68    self.__shx_insert(*args, **kwargs)
69    _streamAutoMerge(self, *args, **kwargs)
70
71def streamAutoMergeAdd(self, *args, **kwargs):
72    self.__shx_add__(*args, **kwargs)
73    _streamAutoMerge(self, *args, **kwargs)
74
75def tracePrepareDataForImage(self, width, height, timescale=None, zoom=1, norm="window"):
76    """
77    Preparing trace data for fast plotting. Using numpy's minmax feature - this
78    will not just downsample the data!
79
80    width : total width of plotting window in pixels
81    height : hight of plotting window in pixels
82    zoom : global zoom factor (y extension)
83    timescale : extension of current timescale (tuple of min and max)
84    norm : norm method: "window", "total" or value (effects y extension)
85    """
86    msgs.sendMessage('log.debug.patches',
87                                message=["Preparing plotting data...", self.id])
88
89    if not timescale:
90        # use trace's total dimensions
91        windowstart, windowend = (self.stats.starttime, self.stats.endtime)
92    else:
93        windowstart, windowend = timescale
94
95    # get slice for time window
96    pt = self.slice(windowstart, windowend)
97
98    duration_total = windowend - windowstart
99    duration_trace = pt.stats.endtime - pt.stats.starttime
100
101    # width of the total screen that is covered by trace data (pixels)
102    pixel_width = duration_trace * width / duration_total
103
104    # remember values for caching
105    CacheTraceID = \
106              (windowstart, windowend, "%.5f" % pixel_width, height, zoom, norm)
107    try:
108        cachedX = CacheTraceID[:3] == self.shx.CacheTraceID[:3]
109        cachedY = cachedX and CacheTraceID[3:] == self.shx.CacheTraceID[3:]
110    except:
111        cachedX = False
112        cachedY = False
113
114    if not cachedX:
115        # save new ID
116        self.shx.CacheTraceID = CacheTraceID
117
118        # data points per pixel
119        npts = pt.stats.npts
120        dpp = int(npts / pixel_width)
121
122        # get copy of data
123        self.shx.PlotData = pt.data.copy()
124    else:
125        msgs.sendMessage('log.devel.patches', message=["using cachedX data for",
126                                                              CacheTraceID[:3]])
127
128    # use minmax approach if more than 4 data points per pixel
129    # self.shx_plotdata will be changed
130    if not cachedX and dpp >= 4:
131        # do integer division since array shape values cannot be floats
132        dimx = npts // dpp
133        covered = dpp * dimx
134
135        # get data for reshape operation
136        data = self.shx.PlotData[:covered]
137
138        # extra treatment for remaining values
139        remaining = self.shx.PlotData[covered:]
140
141        # reshape data and get min/max values
142        data.shape = dimx, dpp
143        _min = data.min(axis=1)
144        _max = data.max(axis=1)
145        # combine data
146        joined = np.append([_min], [_max], axis=0).T.flatten()
147        # handle remaining
148        if len(remaining):
149            joined = np.append(joined, [remaining.min(), remaining.max()])
150
151        msgs.sendMessage(
152            'log.devel.patches',
153            message=[
154                 "pixel_width", pixel_width, "minmax", "npts", npts, "dpp",
155                 dpp, "dimx", dimx, "covered", covered, "len_rest",
156                 len(remaining), "data_len", len(data), "joined", len(joined),
157                 "width", width
158                ]
159        )
160
161        # At this stage the x-transformed data can be cached! If pixel_width
162        # doesn't change, the data can be reused to save time.
163        self.shx.PlotData = joined
164
165    msgs.sendMessage('log.devel.patches',
166                         message=[width, height, norm, type(self.shx.PlotData)])
167
168    if cachedY:
169        msgs.sendMessage('log.devel.patches',
170                           message=["using cachedY data for", CacheTraceID[3:]])
171        return
172   
173    # renew cache id
174    self.shx.CacheTraceID = CacheTraceID
175
176    # get basis for normation
177    if hasattr(norm, "lower"):
178        norm = norm.lower()
179        if norm == "window":
180            norm = abs(pt.max())
181        elif norm == "total":
182            norm = self.max()
183        else:
184            raise ValueError("Invalid input for normation!")
185
186    # Calculate y extension, leads to raw "image" data.
187    # Use a copy to save time if only y changes occur (e.g. zooming).
188    # Apply normation, calibration and total height factor
189    y = self.shx.PlotData.copy()
190    y *= self.stats.calib / norm * height / 2 * zoom
191
192#    print self.shxPlotData.min(), self.shxPlotData.max(), y.min(), y.max()
193
194    # factor for x stretching (trade-off between pixel_width and array shape)
195    stretch = pixel_width / len(y)
196    width = int(math.ceil(pixel_width))
197
198    # calculate line data
199    # do not rely on framework axis' management -> shift/flip y-axis
200    offset = height // 2
201    lines = []
202    oldx, oldy = 0, -y[0] + offset
203
204    for i in xrange(1, len(y)):
205        newx = i * stretch
206        newy = -y[i] + offset
207        lines.append([oldx, oldy, newx, newy])
208        oldx, oldy = newx, newy
209
210    self.shx.ImageData = lines
211    self.shx.PlotPixels = pixel_width
212
213def traceAddShxInit(self, *args, **kwargs):
214    self.__shx_init__(*args, **kwargs)
215
216    # container for SHX releated data
217    self.shx = AttribDict()
218
219# monkey patching obspy stream class: traces are merge automatically if stream
220# get's altered.
221Stream.__shx_init__, Stream.__init__ = Stream.__init__, streamAutoMergeInit
222Stream.__shx_append, Stream.append = Stream.append, streamAutoMergeAppend
223Stream.__shx_extend, Stream.extend = Stream.extend, streamAutoMergeExtend
224Stream.__shx_insert, Stream.insert = Stream.insert, streamAutoMergeInsert
225Stream.__shx_add__, Stream.__add__ = Stream.__add__, streamAutoMergeAdd
226
227# monkey patching obspy trace class: add shx container and method for fast
228# plotting
229Trace.__shx_init__, Trace.__init__ = Trace.__init__, traceAddShxInit
230Trace._shxPrepareImageData = tracePrepareDataForImage
231
232# just an indicator
233patched = True
Note: See TracBrowser for help on using the repository browser.