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

Revision 395, 7.0 KB checked in by marcus, 13 years ago (diff)
  • added controls to plotting test
  • caching of reduced plotting data (2 stages)
  • example from web
  • 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
32#from obspy.core import UTCDateTime
33
34__all__ = [
35    "patched",
36]
37
38def _streamAutoMerge(self, *args, **kwargs):
39    """
40    Call obspy's Stream merge method to fill up gaps using the latest pre-gap
41    sample value. This destroys the spectra of the data but it's common.
42    """
43    if self.traces:
44        self.merge(method=1, fill_value='latest')
45
46def streamAutoMergeInit(self, *args, **kwargs):
47    self.__shx_init__(*args, **kwargs)
48    _streamAutoMerge(self)
49
50def streamAutoMergeAppend(self, *args, **kwargs):
51    self.__shx_append(*args, **kwargs)
52    _streamAutoMerge(self, *args, **kwargs)
53
54def streamAutoMergeExtend(self, *args, **kwargs):
55    self.__shx_extend(*args, **kwargs)
56    _streamAutoMerge(self, *args, **kwargs)
57
58def streamAutoMergeInsert(self, *args, **kwargs):
59    self.__shx_insert(*args, **kwargs)
60    _streamAutoMerge(self, *args, **kwargs)
61
62def streamAutoMergeAdd(self, *args, **kwargs):
63    self.__shx_add__(*args, **kwargs)
64    _streamAutoMerge(self, *args, **kwargs)
65
66def tracePrepareDataForImage(self, width, height=None, timescale=None, zoom=1, norm="window"):
67    """
68    Preparing trace data for fast plotting. Using numpy's minmax feature - this
69    will not just downsample the data!
70
71    width : total width of plotting window in pixels
72    height : hight of plotting window in pixels
73    zoom : global zoom factor (y extension)
74    timescale : extension of current timescale (tuple of min and max)
75    norm : norm method: "window", "total" or value (effects y extension)
76    """
77    if not timescale:
78        # use trace's total dimensions
79        windowstart, windowend = (self.stats.starttime, self.stats.endtime)
80    else:
81        windowstart, windowend = timescale
82
83    # XXX
84    if not height:
85        height = 1000
86
87    # get slice for time window
88    pt = self.slice(windowstart, windowend)
89
90    duration_total = windowend - windowstart
91    duration_trace = pt.stats.endtime - pt.stats.starttime
92
93    # width of the total screen that is covered by trace data (pixels)
94    pixel_width = duration_trace * width / duration_total
95
96    # remember values for caching
97    CacheTraceID = \
98              (windowstart, windowend, "%.5f" % pixel_width, height, zoom, norm)
99    try:
100        cachedX = CacheTraceID[:3] == self.shxCacheTraceID[:3]
101        cachedY = cachedX and CacheTraceID[3:] == self.shxCacheTraceID[3:]
102    except:
103        cachedX = False
104        cachedY = False
105
106    if not cachedX:
107        # save new ID
108        self.shxCacheTraceID = CacheTraceID
109
110        # data points per pixel
111        npts = pt.stats.npts
112        dpp = int(npts / pixel_width)
113
114        # get copy of data
115        self.shxPlotData = pt.data.copy()
116    else:
117        print "using cachedX data for", CacheTraceID[:3]
118
119    # use minmax approach if more than 4 data points per pixel
120    # self.shx_plotdata will be changed
121    if not cachedX and dpp >= 4:
122        # do integer division since array shape values cannot be floats
123        dimx = npts // dpp
124        covered = dpp * dimx
125
126        # get data for reshape operation
127        data = self.shxPlotData[:covered]
128
129        # extra treatment for remaining values
130        remaining = self.shxPlotData[covered:]
131
132        # reshape data and get min/max values
133        data.shape = dimx, dpp
134        _min = data.min(axis=1)
135        _max = data.max(axis=1)
136        # combine data
137        joined = np.append([_min], [_max], axis=0).T.flatten()
138        # handle remaining
139        if len(remaining):
140            joined = np.append(joined, [remaining.min(), remaining.max()])
141
142        print "pixel_width", pixel_width, "minmax", "npts", npts, "dpp", dpp, "dimx", dimx, \
143              "covered", covered, "len_rest", len(remaining), "data_len", \
144              len(data), "joined", len(joined), "width", width
145
146        # At this stage the x-transformed data can be cached! If pixel_width
147        # doesn't change, the data can be reused to save time.
148        self.shxPlotData = joined
149
150    print width, height, norm, type(self.shxPlotData)
151
152    if cachedY:
153        print "using cachedY data for", CacheTraceID[3:]
154        return
155
156    # get basis for normation
157    if hasattr(norm, "lower"):
158        norm = norm.lower()
159        if norm == "window":
160            norm = abs(pt.max())
161        elif norm == "total":
162            norm = self.max()
163        else:
164            raise ValueError("Invalid input for normation!")
165
166    # Calculate y extension, leads to raw "image" data.
167    # Use a copy to save time if only y changes occur (e.g. zooming).
168    # Apply normation, calibration and total height factor
169    y = self.shxPlotData.copy()
170    y *= self.stats.calib / norm * height / 2 * zoom
171
172#    print self.shxPlotData.min(), self.shxPlotData.max(), y.min(), y.max()
173
174    # factor for x stretching (trade-off between pixel_width and array shape)
175    stretch = pixel_width / len(y)
176    width = int(math.ceil(pixel_width))
177
178    # calculate line data
179    # do not rely on framework axis' management -> shift/flip y-axis
180    offset = height // 2
181    lines = []
182    oldx, oldy = 0, -y[0] + offset
183
184    for i in xrange(1, len(y)):
185        newx = i * stretch
186        newy = -y[i] + offset
187        lines.append([oldx, oldy, newx, newy])
188        oldx, oldy = newx, newy
189
190    self.shxImageData = lines
191
192def __traceX(parameters):
193    """
194    Only the x axis is adopted here. Zoom, norm etc. regarding the y extension
195    is done ...
196    """
197    pass
198
199# monkey patching obspy stream class: traces are merge automatically if stream
200# get's altered.
201Stream.__shx_init__, Stream.__init__ = Stream.__init__, streamAutoMergeInit
202Stream.__shx_append, Stream.append = Stream.append, streamAutoMergeAppend
203Stream.__shx_extend, Stream.extend = Stream.extend, streamAutoMergeExtend
204Stream.__shx_insert, Stream.insert = Stream.insert, streamAutoMergeInsert
205Stream.__shx_add__, Stream.__add__ = Stream.__add__, streamAutoMergeAdd
206
207# add method for faster plotting
208Trace.shxPrepareImageData = tracePrepareDataForImage
209
210# just an indicator
211patched = True
Note: See TracBrowser for help on using the repository browser.